8254162: Implementation of Foreign-Memory Access API (Third Incubator)

Reviewed-by: erikj, psandoz, alanb
This commit is contained in:
Maurizio Cimadamore 2020-11-12 16:37:23 +00:00
parent c6ab0fdb15
commit 3e70aac5cc
82 changed files with 6038 additions and 2837 deletions

View File

@ -35,6 +35,7 @@ include gensrc/GensrcExceptions.gmk
include gensrc/GensrcVarHandles.gmk
include gensrc/GensrcModuleLoaderMap.gmk
include gensrc/GensrcEmojiData.gmk
include gensrc/GensrcScopedMemoryAccess.gmk
# GensrcLocaleData.gmk does not set TARGETS, so we must choose which targets
# to include.

View File

@ -0,0 +1,151 @@
#
# 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.
#
SCOPED_MEMORY_ACCESS_GENSRC_DIR := $(SUPPORT_OUTPUTDIR)/gensrc/java.base/jdk/internal/misc
SCOPED_MEMORY_ACCESS_SRC_DIR := $(TOPDIR)/src/java.base/share/classes/jdk/internal/misc
SCOPED_MEMORY_ACCESS_TEMPLATE := $(SCOPED_MEMORY_ACCESS_SRC_DIR)/X-ScopedMemoryAccess.java.template
SCOPED_MEMORY_ACCESS_BIN_TEMPLATE := $(SCOPED_MEMORY_ACCESS_SRC_DIR)/X-ScopedMemoryAccess-bin.java.template
SCOPED_MEMORY_ACCESS_DEST := $(SCOPED_MEMORY_ACCESS_GENSRC_DIR)/ScopedMemoryAccess.java
################################################################################
# Setup a rule for generating the ScopedMemoryAccess java class
# Param 1 - Variable declaration prefix
# Param 2 - Type with first letter capitalized
define GenerateScopedOp
$1_Type := $2
ifeq ($$($1_Type), Byte)
$1_type := byte
$1_BoxType := $$($1_Type)
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -Kbyte
endif
ifeq ($$($1_Type), Short)
$1_type := short
$1_BoxType := $$($1_Type)
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -KUnaligned
endif
ifeq ($$($1_Type), Char)
$1_type := char
$1_BoxType := Character
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -KUnaligned
endif
ifeq ($$($1_Type), Int)
$1_type := int
$1_BoxType := Integer
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -KCAS
$1_ARGS += -KAtomicAdd
$1_ARGS += -KBitwise
$1_ARGS += -KUnaligned
endif
ifeq ($$($1_Type), Long)
$1_type := long
$1_BoxType := $$($1_Type)
$1_rawType := $$($1_type)
$1_RawType := $$($1_Type)
$1_RawBoxType := $$($1_BoxType)
$1_ARGS += -KCAS
$1_ARGS += -KAtomicAdd
$1_ARGS += -KBitwise
$1_ARGS += -KUnaligned
endif
ifeq ($$($1_Type), Float)
$1_type := float
$1_BoxType := $$($1_Type)
$1_rawType := int
$1_RawType := Int
$1_RawBoxType := Integer
$1_ARGS += -KCAS
$1_ARGS += -KfloatingPoint
endif
ifeq ($$($1_Type), Double)
$1_type := double
$1_BoxType := $$($1_Type)
$1_rawType := long
$1_RawType := Long
$1_RawBoxType := Long
$1_ARGS += -KCAS
$1_ARGS += -KfloatingPoint
endif
ifneq ($$(findstring $$($1_Type), Byte Short Char Int Long Float Double), )
$1_ARGS += -KAtomicAdd
endif
ifneq ($$(findstring $$($1_Type), Boolean Byte Short Char Int Long), )
$1_ARGS += -KBitwise
endif
ifneq ($$(findstring $$($1_Type), Byte Short Char), )
$1_ARGS += -KShorterThanInt
endif
endef
################################################################################
# Setup a rule for generating the ScopedMemoryAccess java class
SCOPE_MEMORY_ACCESS_TYPES := Byte Short Char Int Long Float Double
$(foreach t, $(SCOPE_MEMORY_ACCESS_TYPES), \
$(eval $(call GenerateScopedOp,BIN_$t,$t)))
$(SCOPED_MEMORY_ACCESS_DEST): $(BUILD_TOOLS_JDK) $(SCOPED_MEMORY_ACCESS_TEMPLATE) $(SCOPED_MEMORY_ACCESS_BIN_TEMPLATE)
$(call MakeDir, $(SCOPED_MEMORY_ACCESS_GENSRC_DIR))
$(CP) $(SCOPED_MEMORY_ACCESS_TEMPLATE) $(SCOPED_MEMORY_ACCESS_DEST)
$(foreach t, $(SCOPE_MEMORY_ACCESS_TYPES), \
$(TOOL_SPP) -nel -K$(BIN_$t_type) -Dtype=$(BIN_$t_type) -DType=$(BIN_$t_Type) $(BIN_$t_ARGS) \
-i$(SCOPED_MEMORY_ACCESS_BIN_TEMPLATE) -o$(SCOPED_MEMORY_ACCESS_DEST) ;)
$(PRINTF) "}\n" >> $(SCOPED_MEMORY_ACCESS_DEST)
TARGETS += $(SCOPED_MEMORY_ACCESS_DEST)

View File

@ -1086,6 +1086,7 @@ public:
_method_InjectedProfile,
_method_LambdaForm_Compiled,
_method_Hidden,
_method_Scoped,
_method_IntrinsicCandidate,
_jdk_internal_vm_annotation_Contended,
_field_Stable,
@ -2117,6 +2118,11 @@ AnnotationCollector::annotation_index(const ClassLoaderData* loader_data,
if (!privileged) break; // only allow in privileged code
return _method_Hidden;
}
case VM_SYMBOL_ENUM_NAME(jdk_internal_misc_Scoped_signature): {
if (_location != _in_method) break; // only allow for methods
if (!privileged) break; // only allow in privileged code
return _method_Scoped;
}
case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_IntrinsicCandidate_signature): {
if (_location != _in_method) break; // only allow for methods
if (!privileged) break; // only allow in privileged code
@ -2174,6 +2180,8 @@ void MethodAnnotationCollector::apply_to(const methodHandle& m) {
m->set_intrinsic_id(vmIntrinsics::_compiledLambdaForm);
if (has_annotation(_method_Hidden))
m->set_hidden(true);
if (has_annotation(_method_Scoped))
m->set_scoped(true);
if (has_annotation(_method_IntrinsicCandidate) && !m->is_synthetic())
m->set_intrinsic_candidate(true);
if (has_annotation(_jdk_internal_vm_annotation_ReservedStackAccess))

View File

@ -302,6 +302,7 @@
template(jdk_internal_vm_annotation_DontInline_signature, "Ljdk/internal/vm/annotation/DontInline;") \
template(jdk_internal_vm_annotation_ForceInline_signature, "Ljdk/internal/vm/annotation/ForceInline;") \
template(jdk_internal_vm_annotation_Hidden_signature, "Ljdk/internal/vm/annotation/Hidden;") \
template(jdk_internal_misc_Scoped_signature, "Ljdk/internal/misc/ScopedMemoryAccess$Scoped;") \
template(jdk_internal_vm_annotation_IntrinsicCandidate_signature, "Ljdk/internal/vm/annotation/IntrinsicCandidate;") \
template(jdk_internal_vm_annotation_Stable_signature, "Ljdk/internal/vm/annotation/Stable;") \
/* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \

View File

@ -91,7 +91,8 @@ class Method : public Metadata {
_has_injected_profile = 1 << 4,
_running_emcp = 1 << 5,
_intrinsic_candidate = 1 << 6,
_reserved_stack_access = 1 << 7
_reserved_stack_access = 1 << 7,
_scoped = 1 << 8
};
mutable u2 _flags;
@ -901,6 +902,14 @@ public:
_flags = x ? (_flags | _hidden) : (_flags & ~_hidden);
}
bool is_scoped() const {
return (_flags & _scoped) != 0;
}
void set_scoped(bool x) {
_flags = x ? (_flags | _scoped) : (_flags & ~_scoped);
}
bool intrinsic_candidate() {
return (_flags & _intrinsic_candidate) != 0;
}

View File

@ -39,6 +39,7 @@
#include "prims/jvmtiExport.hpp"
#include "prims/nativeLookup.hpp"
#include "prims/unsafe.hpp"
#include "prims/scopedMemoryAccess.hpp"
#include "runtime/arguments.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
@ -240,6 +241,7 @@ static JNINativeMethod lookup_special_native_methods[] = {
#if INCLUDE_JFR
{ CC"Java_jdk_jfr_internal_JVM_registerNatives", NULL, FN_PTR(jfr_register_natives) },
#endif
{ CC"Java_jdk_internal_misc_ScopedMemoryAccess_registerNatives", NULL, FN_PTR(JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods) },
};
static address lookup_special_native(const char* jni_name) {

View File

@ -0,0 +1,186 @@
/*
* 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.
*
* 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.
*
*/
#include "precompiled.hpp"
#include "jni.h"
#include "jvm.h"
#include "classfile/vmSymbols.hpp"
#include "oops/access.inline.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/vframe.inline.hpp"
#include "runtime/deoptimization.hpp"
#include "prims/stackwalk.hpp"
class CloseScopedMemoryFindOopClosure : public OopClosure {
oop _deopt;
bool _found;
public:
CloseScopedMemoryFindOopClosure(jobject deopt) :
_deopt(JNIHandles::resolve(deopt)),
_found(false) {}
template <typename T>
void do_oop_work(T* p) {
if (_found) {
return;
}
if (RawAccess<>::oop_load(p) == _deopt) {
_found = true;
}
}
virtual void do_oop(oop* p) {
do_oop_work(p);
}
virtual void do_oop(narrowOop* p) {
do_oop_work(p);
}
bool found() {
return _found;
}
};
class CloseScopedMemoryClosure : public HandshakeClosure {
jobject _deopt;
jobject _exception;
public:
jboolean _found;
CloseScopedMemoryClosure(jobject deopt, jobject exception)
: HandshakeClosure("CloseScopedMemory")
, _deopt(deopt)
, _exception(exception)
, _found(false) {}
void do_thread(Thread* thread) {
JavaThread* jt = (JavaThread*)thread;
if (!jt->has_last_Java_frame()) {
return;
}
frame last_frame = jt->last_frame();
RegisterMap register_map(jt, true);
if (last_frame.is_safepoint_blob_frame()) {
last_frame = last_frame.sender(&register_map);
}
ResourceMark rm;
if (_deopt != NULL && last_frame.is_compiled_frame() && last_frame.can_be_deoptimized()) {
CloseScopedMemoryFindOopClosure cl(_deopt);
CompiledMethod* cm = last_frame.cb()->as_compiled_method();
/* FIXME: this doesn't work if reachability fences are violated by C2
last_frame.oops_do(&cl, NULL, &register_map);
if (cl.found()) {
//Found the deopt oop in a compiled method; deoptimize.
Deoptimization::deoptimize(jt, last_frame);
}
so... we unconditionally deoptimize, for now: */
Deoptimization::deoptimize(jt, last_frame);
}
const int max_critical_stack_depth = 10;
int depth = 0;
for (vframeStream stream(jt); !stream.at_end(); stream.next()) {
Method* m = stream.method();
if (m->is_scoped()) {
StackValueCollection* locals = stream.asJavaVFrame()->locals();
for (int i = 0; i < locals->size(); i++) {
StackValue* var = locals->at(i);
if (var->type() == T_OBJECT) {
if (var->get_obj() == JNIHandles::resolve(_deopt)) {
assert(depth < max_critical_stack_depth, "can't have more than %d critical frames", max_critical_stack_depth);
_found = true;
return;
}
}
}
break;
}
depth++;
#ifndef ASSERT
if (depth >= max_critical_stack_depth) {
break;
}
#endif
}
}
};
/*
* This function issues a global handshake operation with all
* Java threads. This is useful for implementing asymmetric
* dekker synchronization schemes, where expensive synchronization
* in performance sensitive common paths, may be shifted to
* a less common slow path instead.
* Top frames containg obj will be deoptimized.
*/
JVM_ENTRY(jboolean, ScopedMemoryAccess_closeScope(JNIEnv *env, jobject receiver, jobject deopt, jobject exception))
CloseScopedMemoryClosure cl(deopt, exception);
Handshake::execute(&cl);
return !cl._found;
JVM_END
/// JVM_RegisterUnsafeMethods
#define PKG "Ljdk/internal/misc/"
#define MEMACCESS "ScopedMemoryAccess"
#define SCOPE PKG MEMACCESS "$Scope;"
#define SCOPED_ERR PKG MEMACCESS "$Scope$ScopedAccessError;"
#define CC (char*) /*cast a literal from (const char*)*/
#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f)
static JNINativeMethod jdk_internal_misc_ScopedMemoryAccess_methods[] = {
{CC "closeScope0", CC "(" SCOPE SCOPED_ERR ")Z", FN_PTR(ScopedMemoryAccess_closeScope)},
};
#undef CC
#undef FN_PTR
#undef PKG
#undef MEMACCESS
#undef SCOPE
#undef SCOPED_EXC
// This function is exported, used by NativeLookup.
JVM_ENTRY(void, JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods(JNIEnv *env, jclass scopedMemoryAccessClass))
ThreadToNativeFromVM ttnfv(thread);
int ok = env->RegisterNatives(scopedMemoryAccessClass, jdk_internal_misc_ScopedMemoryAccess_methods, sizeof(jdk_internal_misc_ScopedMemoryAccess_methods)/sizeof(JNINativeMethod));
guarantee(ok == 0, "register jdk.internal.misc.ScopedMemoryAccess natives");
JVM_END

View File

@ -0,0 +1,35 @@
/*
* 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.
*
* 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.
*
*/
#ifndef SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP
#define SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP
#include "jni.h"
extern "C" {
void JNICALL JVM_RegisterJDKInternalMiscScopedMemoryAccessMethods(JNIEnv *env, jobject rec, jobject scope, jthrowable exception);
}
#endif // SHARE_PRIMS_SCOPED_MEMORY_ACCESS_HPP

View File

@ -36,28 +36,21 @@ abstract class MemoryAccessVarHandleBase extends VarHandle {
/** access size (in bytes, computed from var handle carrier type) **/
final long length;
/** access offset (in bytes); must be compatible with {@code alignmentMask} **/
final long offset;
/** alignment constraint (in bytes, expressed as a bit mask) **/
final long alignmentMask;
MemoryAccessVarHandleBase(VarForm form, boolean be, long length, long offset, long alignmentMask, boolean exact) {
/** if true, only the base part of the address will be checked for alignment **/
final boolean skipAlignmentMaskCheck;
MemoryAccessVarHandleBase(VarForm form, boolean skipAlignmentMaskCheck, boolean be, long length, long alignmentMask, boolean exact) {
super(form, exact);
this.skipAlignmentMaskCheck = skipAlignmentMaskCheck;
this.be = be;
this.length = length;
this.offset = offset;
this.alignmentMask = alignmentMask;
}
static IllegalStateException newIllegalStateExceptionForMisalignedAccess(long address) {
return new IllegalStateException("Misaligned access at address: " + address);
}
/**
* Strides used for multi-dimensional access; each stride must be compatible with {@code alignmentMask}.
*/
abstract long[] strides();
abstract Class<?> carrier();
}

View File

@ -1,554 +0,0 @@
/*
* Copyright (c) 2019, 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.lang.invoke;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.ConstantDynamic;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.internal.vm.annotation.ForceInline;
import sun.security.action.GetBooleanAction;
import sun.security.action.GetPropertyAction;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import static jdk.internal.org.objectweb.asm.Opcodes.AALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_2;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_3;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
import static jdk.internal.org.objectweb.asm.Opcodes.NEWARRAY;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
import static jdk.internal.org.objectweb.asm.Opcodes.V14;
class MemoryAccessVarHandleGenerator {
private static final String DEBUG_DUMP_CLASSES_DIR_PROPERTY = "jdk.internal.foreign.ClassGenerator.DEBUG_DUMP_CLASSES_DIR";
private static final boolean DEBUG =
GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.ClassGenerator.DEBUG");
private static final Class<?> BASE_CLASS = MemoryAccessVarHandleBase.class;
private static final HashMap<Class<?>, Class<?>> helperClassCache;
private final static MethodType OFFSET_OP_TYPE;
private final static MethodHandle ADD_OFFSETS_HANDLE;
private final static MethodHandle MUL_OFFSETS_HANDLE;
private final static MethodType CONSTR_TYPE = MethodType.methodType(void.class, VarForm.class,
boolean.class, long.class, long.class, long.class, boolean.class, long[].class);
// MemoryAccessVarHandleBase
private final static MethodType SUPER_CONTR_TYPE = MethodType.methodType(void.class, VarForm.class,
boolean.class, long.class, long.class, long.class, boolean.class);
static {
helperClassCache = new HashMap<>();
helperClassCache.put(byte.class, MemoryAccessVarHandleByteHelper.class);
helperClassCache.put(short.class, MemoryAccessVarHandleShortHelper.class);
helperClassCache.put(char.class, MemoryAccessVarHandleCharHelper.class);
helperClassCache.put(int.class, MemoryAccessVarHandleIntHelper.class);
helperClassCache.put(long.class, MemoryAccessVarHandleLongHelper.class);
helperClassCache.put(float.class, MemoryAccessVarHandleFloatHelper.class);
helperClassCache.put(double.class, MemoryAccessVarHandleDoubleHelper.class);
OFFSET_OP_TYPE = MethodType.methodType(long.class, long.class, long.class, MemoryAddressProxy.class);
try {
ADD_OFFSETS_HANDLE = MethodHandles.Lookup.IMPL_LOOKUP.findStatic(MemoryAddressProxy.class, "addOffsets", OFFSET_OP_TYPE);
MUL_OFFSETS_HANDLE = MethodHandles.Lookup.IMPL_LOOKUP.findStatic(MemoryAddressProxy.class, "multiplyOffsets", OFFSET_OP_TYPE);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
private static final File DEBUG_DUMP_CLASSES_DIR;
static {
String path = GetPropertyAction.privilegedGetProperty(DEBUG_DUMP_CLASSES_DIR_PROPERTY);
if (path == null) {
DEBUG_DUMP_CLASSES_DIR = null;
} else {
DEBUG_DUMP_CLASSES_DIR = new File(path);
}
}
private final String implClassName;
private final int dimensions;
private final Class<?> carrier;
private final Class<?> helperClass;
private final VarForm form;
private final Object[] classData;
MemoryAccessVarHandleGenerator(Class<?> carrier, int dims) {
this.dimensions = dims;
this.carrier = carrier;
Class<?>[] components = new Class<?>[dimensions];
Arrays.fill(components, long.class);
this.form = new VarForm(BASE_CLASS, MemoryAddressProxy.class, carrier, components);
this.helperClass = helperClassCache.get(carrier);
this.implClassName = internalName(helperClass) + dimensions;
// live constants
Class<?>[] intermediate = new Class<?>[dimensions];
Arrays.fill(intermediate, long.class);
this.classData = new Object[] { carrier, intermediate, ADD_OFFSETS_HANDLE, MUL_OFFSETS_HANDLE };
}
/*
* Generate a VarHandle memory access factory.
* The factory has type (ZJJ[J)VarHandle.
*/
MethodHandle generateHandleFactory() {
byte[] classBytes = generateClassBytes();
if (DEBUG_DUMP_CLASSES_DIR != null) {
debugWriteClassToFile(classBytes);
}
try {
MethodHandles.Lookup lookup = MethodHandles.lookup().defineHiddenClassWithClassData(classBytes, classData);
Class<?> implCls = lookup.lookupClass();
Class<?>[] components = new Class<?>[dimensions];
Arrays.fill(components, long.class);
VarForm form = new VarForm(implCls, MemoryAddressProxy.class, carrier, components);
MethodHandle constr = lookup.findConstructor(implCls, CONSTR_TYPE);
constr = MethodHandles.insertArguments(constr, 0, form);
return constr;
} catch (Throwable ex) {
debugPrintClass(classBytes);
throw new AssertionError(ex);
}
}
/*
* Generate a specialized VarHandle class for given carrier
* and access coordinates.
*/
byte[] generateClassBytes() {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
if (DEBUG) {
System.out.println("Generating header implementation class");
}
cw.visit(V14, ACC_PUBLIC | ACC_SUPER, implClassName, null, Type.getInternalName(BASE_CLASS), null);
//add dimension fields
for (int i = 0; i < dimensions; i++) {
cw.visitField(ACC_PRIVATE | ACC_FINAL, "dim" + i, "J", null, null);
}
addStaticInitializer(cw);
addConstructor(cw);
addAccessModeTypeMethod(cw);
addStridesAccessor(cw);
addCarrierAccessor(cw);
addAsExact(cw);
addAsGeneric(cw);
for (VarHandle.AccessMode mode : VarHandle.AccessMode.values()) {
addAccessModeMethodIfNeeded(mode, cw);
}
cw.visitEnd();
return cw.toByteArray();
}
void addStaticInitializer(ClassWriter cw) {
// carrier and intermediate
cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "carrier", Class.class.descriptorString(), null, null);
cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "intermediate", Class[].class.descriptorString(), null, null);
cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "addHandle", MethodHandle.class.descriptorString(), null, null);
cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, "mulHandle", MethodHandle.class.descriptorString(), null, null);
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
// extract class data in static final fields
MethodType mtype = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class);
Handle bsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData",
mtype.descriptorString(), false);
ConstantDynamic dynamic = new ConstantDynamic("classData", Object[].class.descriptorString(), bsm);
mv.visitLdcInsn(dynamic);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Object[].class));
mv.visitVarInsn(ASTORE, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_0);
mv.visitInsn(AALOAD);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
mv.visitFieldInsn(PUTSTATIC, implClassName, "carrier", Class.class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_1);
mv.visitInsn(AALOAD);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class[].class));
mv.visitFieldInsn(PUTSTATIC, implClassName, "intermediate", Class[].class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_2);
mv.visitInsn(AALOAD);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class));
mv.visitFieldInsn(PUTSTATIC, implClassName, "addHandle", MethodHandle.class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_3);
mv.visitInsn(AALOAD);
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class));
mv.visitFieldInsn(PUTSTATIC, implClassName, "mulHandle", MethodHandle.class.descriptorString());
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addConstructor(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(0, "<init>", CONSTR_TYPE.toMethodDescriptorString(), null, null);
mv.visitCode();
//super call
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1); // vform
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(VarForm.class));
mv.visitVarInsn(ILOAD, 2); // be
mv.visitVarInsn(LLOAD, 3); // length
mv.visitVarInsn(LLOAD, 5); // offset
mv.visitVarInsn(LLOAD, 7); // alignmentMask
mv.visitVarInsn(ILOAD, 9); // exact
mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(BASE_CLASS), "<init>",
SUPER_CONTR_TYPE.toMethodDescriptorString(), false);
//init dimensions
for (int i = 0 ; i < dimensions ; i++) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 10);
mv.visitLdcInsn(i);
mv.visitInsn(LALOAD);
mv.visitFieldInsn(PUTFIELD, implClassName, "dim" + i, "J");
}
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addAccessModeTypeMethod(ClassWriter cw) {
MethodType modeMethType = MethodType.methodType(MethodType.class, VarHandle.AccessType.class);
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "accessModeTypeUncached", modeMethType.toMethodDescriptorString(), null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(Type.getType(MemoryAddressProxy.class));
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(Class.class));
mv.visitFieldInsn(GETSTATIC, implClassName, "carrier", Class.class.descriptorString());
mv.visitFieldInsn(GETSTATIC, implClassName, "intermediate", Class[].class.descriptorString());
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(VarHandle.AccessType.class),
"accessModeType", MethodType.methodType(MethodType.class, Class.class, Class.class, Class[].class).toMethodDescriptorString(), false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addAccessModeMethodIfNeeded(VarHandle.AccessMode mode, ClassWriter cw) {
String methName = mode.methodName();
MethodType methType = form.getMethodType(mode.at.ordinal())
.insertParameterTypes(0, VarHandle.class);
try {
MethodType helperType = methType.insertParameterTypes(2, long.class);
if (dimensions > 0) {
helperType = helperType.dropParameterTypes(3, 3 + dimensions);
}
//try to resolve...
String helperMethodName = methName + "0";
MethodHandles.Lookup.IMPL_LOOKUP
.findStatic(helperClass,
helperMethodName,
helperType);
MethodVisitor mv = cw.visitMethod(ACC_STATIC, methName, methType.toMethodDescriptorString(), null, null);
mv.visitAnnotation(Type.getDescriptor(ForceInline.class), true);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0); // handle impl
mv.visitVarInsn(ALOAD, 1); // receiver
// offset calculation
int slot = 2;
mv.visitVarInsn(ALOAD, 0); // load recv
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(BASE_CLASS));
mv.visitFieldInsn(GETFIELD, Type.getInternalName(BASE_CLASS), "offset", "J");
for (int i = 0 ; i < dimensions ; i++) {
// load ADD MH
mv.visitFieldInsn(GETSTATIC, implClassName, "addHandle", MethodHandle.class.descriptorString());
//fixup stack so that ADD MH ends up bottom
mv.visitInsn(Opcodes.DUP_X2);
mv.visitInsn(Opcodes.POP);
// load MUL MH
mv.visitFieldInsn(GETSTATIC, implClassName, "mulHandle", MethodHandle.class.descriptorString());
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class));
mv.visitVarInsn(ALOAD, 0); // load recv
mv.visitTypeInsn(CHECKCAST, implClassName);
mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J");
mv.visitVarInsn(LLOAD, slot);
mv.visitVarInsn(ALOAD, 1); // receiver
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MemoryAddressProxy.class));
//MUL
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact",
OFFSET_OP_TYPE.toMethodDescriptorString(), false);
mv.visitVarInsn(ALOAD, 1); // receiver
mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MemoryAddressProxy.class));
//ADD
mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact",
OFFSET_OP_TYPE.toMethodDescriptorString(), false);
slot += 2;
}
for (int i = 2 + dimensions; i < methType.parameterCount() ; i++) {
Class<?> param = methType.parameterType(i);
mv.visitVarInsn(loadInsn(param), slot); // load index
slot += getSlotsForType(param);
}
//call helper
mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(helperClass), helperMethodName,
helperType.toMethodDescriptorString(), false);
mv.visitInsn(returnInsn(helperType.returnType()));
mv.visitMaxs(0, 0);
mv.visitEnd();
} catch (ReflectiveOperationException ex) {
//not found, skip
}
}
void addStridesAccessor(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "strides", "()[J", null, null);
mv.visitCode();
iConstInsn(mv, dimensions);
mv.visitIntInsn(NEWARRAY, T_LONG);
for (int i = 0 ; i < dimensions ; i++) {
mv.visitInsn(DUP);
iConstInsn(mv, i);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, implClassName, "dim" + i, "J");
mv.visitInsn(LASTORE);
}
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
void addCarrierAccessor(ClassWriter cw) {
MethodVisitor mv = cw.visitMethod(ACC_FINAL, "carrier", "()Ljava/lang/Class;", null, null);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, implClassName, "carrier", Class.class.descriptorString());
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private void addAsExact(ClassWriter cw) {
addAsExactOrAsGeneric(cw, "asExact", true);
}
private void addAsGeneric(ClassWriter cw) {
addAsExactOrAsGeneric(cw, "asGeneric", false);
}
private void addAsExactOrAsGeneric(ClassWriter cw, String name, boolean exact) {
MethodVisitor mv = cw.visitMethod(ACC_FINAL, name, "()Ljava/lang/invoke/VarHandle;", null, null);
mv.visitCode();
mv.visitTypeInsn(NEW, implClassName);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalName(VarHandle.class), "vform", VarForm.class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "be", boolean.class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "length", long.class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "offset", long.class.descriptorString());
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalName(MemoryAccessVarHandleBase.class), "alignmentMask", long.class.descriptorString());
mv.visitIntInsn(BIPUSH, exact ? 1 : 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, implClassName, "strides", "()[J", false);
mv.visitMethodInsn(INVOKESPECIAL, implClassName, "<init>", CONSTR_TYPE.descriptorString(), false);
mv.visitInsn(ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
// shared code generation helpers
private static int getSlotsForType(Class<?> c) {
if (c == long.class || c == double.class) {
return 2;
}
return 1;
}
private static String internalName(Class<?> cls) {
return cls.getName().replace('.', '/');
}
/**
* Emits an actual return instruction conforming to the given return type.
*/
private int returnInsn(Class<?> type) {
return switch (LambdaForm.BasicType.basicType(type)) {
case I_TYPE -> Opcodes.IRETURN;
case J_TYPE -> Opcodes.LRETURN;
case F_TYPE -> Opcodes.FRETURN;
case D_TYPE -> Opcodes.DRETURN;
case L_TYPE -> Opcodes.ARETURN;
case V_TYPE -> RETURN;
};
}
private int loadInsn(Class<?> type) {
return switch (LambdaForm.BasicType.basicType(type)) {
case I_TYPE -> Opcodes.ILOAD;
case J_TYPE -> LLOAD;
case F_TYPE -> Opcodes.FLOAD;
case D_TYPE -> Opcodes.DLOAD;
case L_TYPE -> Opcodes.ALOAD;
case V_TYPE -> throw new IllegalStateException("Cannot load void");
};
}
private static void iConstInsn(MethodVisitor mv, int i) {
switch (i) {
case -1, 0, 1, 2, 3, 4, 5:
mv.visitInsn(ICONST_0 + i);
break;
default:
if(i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
mv.visitIntInsn(BIPUSH, i);
} else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
mv.visitIntInsn(SIPUSH, i);
} else {
mv.visitLdcInsn(i);
}
}
}
// debug helpers
private static String debugPrintClass(byte[] classFile) {
ClassReader cr = new ClassReader(classFile);
StringWriter sw = new StringWriter();
cr.accept(new TraceClassVisitor(new PrintWriter(sw)), 0);
return sw.toString();
}
private void debugWriteClassToFile(byte[] classFile) {
File file = new File(DEBUG_DUMP_CLASSES_DIR, implClassName + ".class");
if (DEBUG) {
System.err.println("Dumping class " + implClassName + " to " + file);
}
try {
debugWriteDataToFile(classFile, file);
} catch (Exception e) {
throw new RuntimeException("Failed to write class " + implClassName + " to file " + file);
}
}
private void debugWriteDataToFile(byte[] data, File file) {
if (file.exists()) {
file.delete();
}
if (file.exists()) {
throw new RuntimeException("Failed to remove pre-existing file " + file);
}
File parent = file.getParentFile();
if (!parent.exists()) {
parent.mkdirs();
}
if (!parent.exists()) {
throw new RuntimeException("Failed to create " + parent);
}
if (!parent.isDirectory()) {
throw new RuntimeException(parent + " is not a directory");
}
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(data);
} catch (IOException e) {
throw new RuntimeException("Failed to write class " + implClassName + " to file " + file);
}
}
}

View File

@ -1769,40 +1769,9 @@ abstract class MethodHandleImpl {
}
@Override
public VarHandle memoryAccessVarHandle(Class<?> carrier, long alignmentMask,
ByteOrder order, long offset, long[] strides) {
return VarHandles.makeMemoryAddressViewHandle(carrier, alignmentMask, order, offset, strides);
}
@Override
public Class<?> memoryAddressCarrier(VarHandle handle) {
return checkMemoryAccessHandle(handle).carrier();
}
@Override
public long memoryAddressAlignmentMask(VarHandle handle) {
return checkMemoryAccessHandle(handle).alignmentMask;
}
@Override
public ByteOrder memoryAddressByteOrder(VarHandle handle) {
return checkMemoryAccessHandle(handle).be ?
ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
}
@Override
public long memoryAddressOffset(VarHandle handle) {
return checkMemoryAccessHandle(handle).offset;
}
@Override
public long[] memoryAddressStrides(VarHandle handle) {
return checkMemoryAccessHandle(handle).strides();
}
@Override
public boolean isMemoryAccessVarHandle(VarHandle handle) {
return asMemoryAccessVarHandle(handle) != null;
public VarHandle memoryAccessVarHandle(Class<?> carrier, boolean skipAlignmentMaskCheck, long alignmentMask,
ByteOrder order) {
return VarHandles.makeMemoryAddressViewHandle(carrier, skipAlignmentMaskCheck, alignmentMask, order);
}
@Override
@ -1834,26 +1803,6 @@ abstract class MethodHandleImpl {
public VarHandle insertCoordinates(VarHandle target, int pos, Object... values) {
return VarHandles.insertCoordinates(target, pos, values);
}
private MemoryAccessVarHandleBase asMemoryAccessVarHandle(VarHandle handle) {
if (handle instanceof MemoryAccessVarHandleBase) {
return (MemoryAccessVarHandleBase)handle;
} else if (handle.target() instanceof MemoryAccessVarHandleBase) {
// skip first adaptation, since we have to step over MemoryAddressProxy
// see JDK-8237349
return (MemoryAccessVarHandleBase)handle.target();
} else {
return null;
}
}
private MemoryAccessVarHandleBase checkMemoryAccessHandle(VarHandle handle) {
MemoryAccessVarHandleBase base = asMemoryAccessVarHandle(handle);
if (base == null) {
throw new IllegalArgumentException("Not a memory access varhandle: " + handle);
}
return base;
}
});
}

View File

@ -315,30 +315,36 @@ final class VarHandles {
* to a single fixed offset to compute an effective offset from the given MemoryAddress for the access.
*
* @param carrier the Java carrier type.
* @param skipAlignmentMaskCheck if true, only the base part of the address will be checked for alignment.
* @param alignmentMask alignment requirement to be checked upon access. In bytes. Expressed as a mask.
* @param byteOrder the byte order.
* @param offset a constant offset for the access.
* @param strides the scale factors with which to multiply given access coordinates.
* @return the created VarHandle.
*/
static VarHandle makeMemoryAddressViewHandle(Class<?> carrier, long alignmentMask,
ByteOrder byteOrder, long offset, long[] strides) {
static VarHandle makeMemoryAddressViewHandle(Class<?> carrier, boolean skipAlignmentMaskCheck, long alignmentMask,
ByteOrder byteOrder) {
if (!carrier.isPrimitive() || carrier == void.class || carrier == boolean.class) {
throw new IllegalArgumentException("Invalid carrier: " + carrier.getName());
}
long size = Wrapper.forPrimitiveType(carrier).bitWidth() / 8;
boolean be = byteOrder == ByteOrder.BIG_ENDIAN;
boolean exact = false;
Map<Integer, MethodHandle> carrierFactory = ADDRESS_FACTORIES.get(carrier);
MethodHandle fac = carrierFactory.computeIfAbsent(strides.length,
dims -> new MemoryAccessVarHandleGenerator(carrier, dims)
.generateHandleFactory());
try {
boolean exact = false;
return maybeAdapt((VarHandle)fac.invoke(be, size, offset, alignmentMask, exact, strides));
} catch (Throwable ex) {
throw new IllegalStateException(ex);
if (carrier == byte.class) {
return maybeAdapt(new MemoryAccessVarHandleByteHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact));
} else if (carrier == char.class) {
return maybeAdapt(new MemoryAccessVarHandleCharHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact));
} else if (carrier == short.class) {
return maybeAdapt(new MemoryAccessVarHandleShortHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact));
} else if (carrier == int.class) {
return maybeAdapt(new MemoryAccessVarHandleIntHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact));
} else if (carrier == float.class) {
return maybeAdapt(new MemoryAccessVarHandleFloatHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact));
} else if (carrier == long.class) {
return maybeAdapt(new MemoryAccessVarHandleLongHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact));
} else if (carrier == double.class) {
return maybeAdapt(new MemoryAccessVarHandleDoubleHelper(skipAlignmentMaskCheck, be, size, alignmentMask, exact));
} else {
throw new IllegalStateException("Cannot get here");
}
}

View File

@ -27,6 +27,8 @@ package java.lang.invoke;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.misc.ScopedMemoryAccess.Scope;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
@ -41,9 +43,11 @@ import static java.lang.invoke.MethodHandleStatics.UNSAFE;
final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();
static final int ALIGN = $BoxType$.BYTES - 1;
static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
#if[floatingPoint]
@ForceInline
@ -601,13 +605,17 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
@ForceInline
static int index(ByteBuffer bb, int index) {
MemorySegmentProxy segmentProxy = nioAccess.bufferSegment(bb);
if (segmentProxy != null) {
segmentProxy.checkValidState();
}
MemorySegmentProxy segmentProxy = NIO_ACCESS.bufferSegment(bb);
return Preconditions.checkIndex(index, UNSAFE.getInt(bb, BUFFER_LIMIT) - ALIGN, null);
}
@ForceInline
static Scope scope(ByteBuffer bb) {
MemorySegmentProxy segmentProxy = NIO_ACCESS.bufferSegment(bb);
return segmentProxy != null ?
segmentProxy.scope() : null;
}
@ForceInline
static int indexRO(ByteBuffer bb, int index) {
if (UNSAFE.getBoolean(bb, BYTE_BUFFER_IS_READ_ONLY))
@ -628,13 +636,13 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
#if[floatingPoint]
$rawType$ rawValue = UNSAFE.get$RawType$Unaligned(
$rawType$ rawValue = SCOPED_MEMORY_ACCESS.get$RawType$Unaligned(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
((long) index(bb, index)) + UNSAFE.getLong(bb, BUFFER_ADDRESS),
handle.be);
return $Type$.$rawType$BitsTo$Type$(rawValue);
#else[floatingPoint]
return UNSAFE.get$Type$Unaligned(
return SCOPED_MEMORY_ACCESS.get$Type$Unaligned(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
((long) index(bb, index)) + UNSAFE.getLong(bb, BUFFER_ADDRESS),
handle.be);
@ -646,13 +654,13 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
#if[floatingPoint]
UNSAFE.put$RawType$Unaligned(
SCOPED_MEMORY_ACCESS.put$RawType$Unaligned(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
((long) indexRO(bb, index)) + UNSAFE.getLong(bb, BUFFER_ADDRESS),
$Type$.$type$ToRaw$RawType$Bits(value),
handle.be);
#else[floatingPoint]
UNSAFE.put$Type$Unaligned(
SCOPED_MEMORY_ACCESS.put$Type$Unaligned(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
((long) indexRO(bb, index)) + UNSAFE.getLong(bb, BUFFER_ADDRESS),
value,
@ -665,7 +673,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.get$RawType$Volatile(
SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, index(bb, index))));
}
@ -674,7 +682,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static void setVolatile(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
UNSAFE.put$RawType$Volatile(
SCOPED_MEMORY_ACCESS.put$RawType$Volatile(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, value));
@ -685,7 +693,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.get$RawType$Acquire(
SCOPED_MEMORY_ACCESS.get$RawType$Acquire(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, index(bb, index))));
}
@ -694,7 +702,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static void setRelease(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
UNSAFE.put$RawType$Release(
SCOPED_MEMORY_ACCESS.put$RawType$Release(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, value));
@ -705,7 +713,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.get$RawType$Opaque(
SCOPED_MEMORY_ACCESS.get$RawType$Opaque(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, index(bb, index))));
}
@ -714,7 +722,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static void setOpaque(VarHandle ob, Object obb, int index, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
UNSAFE.put$RawType$Opaque(
SCOPED_MEMORY_ACCESS.put$RawType$Opaque(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, value));
@ -726,12 +734,12 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
#if[Object]
return UNSAFE.compareAndSetReference(
return SCOPED_MEMORY_ACCESS.compareAndSetReference(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, expected), convEndian(handle.be, value));
#else[Object]
return UNSAFE.compareAndSet$RawType$(
return SCOPED_MEMORY_ACCESS.compareAndSet$RawType$(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, expected), convEndian(handle.be, value));
@ -743,7 +751,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$(
SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, expected), convEndian(handle.be, value)));
@ -754,7 +762,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Acquire(
SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Acquire(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, expected), convEndian(handle.be, value)));
@ -765,7 +773,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Release(
SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Release(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, expected), convEndian(handle.be, value)));
@ -775,7 +783,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static boolean weakCompareAndSetPlain(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return UNSAFE.weakCompareAndSet$RawType$Plain(
return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Plain(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, expected), convEndian(handle.be, value));
@ -785,7 +793,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static boolean weakCompareAndSet(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return UNSAFE.weakCompareAndSet$RawType$(
return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, expected), convEndian(handle.be, value));
@ -795,7 +803,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static boolean weakCompareAndSetAcquire(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return UNSAFE.weakCompareAndSet$RawType$Acquire(
return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Acquire(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, expected), convEndian(handle.be, value));
@ -805,7 +813,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
static boolean weakCompareAndSetRelease(VarHandle ob, Object obb, int index, $type$ expected, $type$ value) {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return UNSAFE.weakCompareAndSet$RawType$Release(
return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Release(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, expected), convEndian(handle.be, value));
@ -817,13 +825,13 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
#if[Object]
return convEndian(handle.be,
UNSAFE.getAndSetReference(
SCOPED_MEMORY_ACCESS.getAndSetReference(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, value)));
#else[Object]
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$(
SCOPED_MEMORY_ACCESS.getAndSet$RawType$(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, value)));
@ -835,7 +843,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Acquire(
SCOPED_MEMORY_ACCESS.getAndSet$RawType$Acquire(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, value)));
@ -846,7 +854,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Release(
SCOPED_MEMORY_ACCESS.getAndSet$RawType$Release(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
convEndian(handle.be, value)));
@ -859,7 +867,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$(
return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
delta);
@ -873,7 +881,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Acquire(
return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Acquire(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
delta);
@ -887,7 +895,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Release(
return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Release(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
delta);
@ -902,7 +910,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
Object base = UNSAFE.getReference(bb, BYTE_BUFFER_HB);
long offset = address(bb, indexRO(bb, index));
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb), base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue + delta)));
@ -916,7 +924,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$(
return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
value);
@ -930,7 +938,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Release(
return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Release(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
value);
@ -944,7 +952,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Acquire(
return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Acquire(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
value);
@ -959,7 +967,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
Object base = UNSAFE.getReference(bb, BYTE_BUFFER_HB);
long offset = address(bb, indexRO(bb, index));
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb), base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue | value)));
@ -971,7 +979,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$(
return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
value);
@ -985,7 +993,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Release(
return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Release(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
value);
@ -999,7 +1007,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Acquire(
return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Acquire(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
value);
@ -1014,7 +1022,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
Object base = UNSAFE.getReference(bb, BYTE_BUFFER_HB);
long offset = address(bb, indexRO(bb, index));
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb), base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue & value)));
@ -1027,7 +1035,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$(
return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
value);
@ -1041,7 +1049,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Release(
return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Release(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
value);
@ -1055,7 +1063,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
ByteBufferHandle handle = (ByteBufferHandle)ob;
ByteBuffer bb = (ByteBuffer) Objects.requireNonNull(obb);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Acquire(
return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Acquire(scope(bb),
UNSAFE.getReference(bb, BYTE_BUFFER_HB),
address(bb, indexRO(bb, index)),
value);
@ -1070,7 +1078,7 @@ final class VarHandleByteArrayAs$Type$s extends VarHandleByteArrayBase {
Object base = UNSAFE.getReference(bb, BYTE_BUFFER_HB);
long offset = address(bb, indexRO(bb, index));
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(scope(bb), base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue ^ value)));

View File

@ -24,21 +24,51 @@
*/
package java.lang.invoke;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.ref.Reference;
import java.util.Objects;
import static java.lang.invoke.MethodHandleStatics.UNSAFE;
#warn
final class MemoryAccessVarHandle$Type$Helper {
final class MemoryAccessVarHandle$Type$Helper extends MemoryAccessVarHandleBase {
static final boolean BE = UNSAFE.isBigEndian();
static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
static final int VM_ALIGN = $BoxType$.BYTES - 1;
static final VarForm FORM = new VarForm(MemoryAccessVarHandle$Type$Helper.class, MemorySegmentProxy.class, $type$.class, long.class);
MemoryAccessVarHandle$Type$Helper(boolean skipAlignmentMaskCheck, boolean be, long length, long alignmentMask, boolean exact) {
super(FORM, skipAlignmentMaskCheck, be, length, alignmentMask, exact);
}
@Override
final MethodType accessModeTypeUncached(VarHandle.AccessType accessType) {
return accessType.accessModeType(MemorySegmentProxy.class, $type$.class, long.class);
}
@Override
public MemoryAccessVarHandle$Type$Helper withInvokeExactBehavior() {
return hasInvokeExactBehavior() ?
this :
new MemoryAccessVarHandle$Type$Helper(skipAlignmentMaskCheck, be, length, alignmentMask, true);
}
@Override
public MemoryAccessVarHandle$Type$Helper withInvokeBehavior() {
return !hasInvokeExactBehavior() ?
this :
new MemoryAccessVarHandle$Type$Helper(skipAlignmentMaskCheck, be, length, alignmentMask, true);
}
#if[floatingPoint]
@ForceInline
static $rawType$ convEndian(boolean big, $type$ v) {
@ -66,15 +96,15 @@ final class MemoryAccessVarHandle$Type$Helper {
#end[floatingPoint]
@ForceInline
static MemoryAddressProxy checkAddress(Object obb, long offset, long length, boolean ro) {
MemoryAddressProxy oo = (MemoryAddressProxy)Objects.requireNonNull(obb);
static MemorySegmentProxy checkAddress(Object obb, long offset, long length, boolean ro) {
MemorySegmentProxy oo = (MemorySegmentProxy)Objects.requireNonNull(obb);
oo.checkAccess(offset, length, ro);
return oo;
}
@ForceInline
static long offset(MemoryAddressProxy bb, long offset, long alignmentMask) {
long address = offsetNoVMAlignCheck(bb, offset, alignmentMask);
static long offset(boolean skipAlignmentMaskCheck, MemorySegmentProxy bb, long offset, long alignmentMask) {
long address = offsetNoVMAlignCheck(skipAlignmentMaskCheck, bb, offset, alignmentMask);
if ((address & VM_ALIGN) != 0) {
throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address);
}
@ -82,60 +112,66 @@ final class MemoryAccessVarHandle$Type$Helper {
}
@ForceInline
static long offsetNoVMAlignCheck(MemoryAddressProxy bb, long offset, long alignmentMask) {
static long offsetNoVMAlignCheck(boolean skipAlignmentMaskCheck, MemorySegmentProxy bb, long offset, long alignmentMask) {
long base = bb.unsafeGetOffset();
long address = base + offset;
//note: the offset portion has already been aligned-checked, by construction
if ((base & alignmentMask) != 0) {
throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address);
if (skipAlignmentMaskCheck) {
//note: the offset portion has already been aligned-checked, by construction
if ((base & alignmentMask) != 0) {
throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address);
}
} else {
if ((address & alignmentMask) != 0) {
throw MemoryAccessVarHandleBase.newIllegalStateExceptionForMisalignedAccess(address);
}
}
return address;
}
@ForceInline
static $type$ get0(VarHandle ob, Object obb, long base) {
static $type$ get(VarHandle ob, Object obb, long base) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, true);
#if[floatingPoint]
$rawType$ rawValue = UNSAFE.get$RawType$Unaligned(
$rawType$ rawValue = SCOPED_MEMORY_ACCESS.get$RawType$Unaligned(bb.scope(),
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
handle.be);
return $Type$.$rawType$BitsTo$Type$(rawValue);
#else[floatingPoint]
#if[byte]
return UNSAFE.get$Type$(
return SCOPED_MEMORY_ACCESS.get$Type$(bb.scope(),
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask));
offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask));
#else[byte]
return UNSAFE.get$Type$Unaligned(
return SCOPED_MEMORY_ACCESS.get$Type$Unaligned(bb.scope(),
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
handle.be);
#end[byte]
#end[floatingPoint]
}
@ForceInline
static void set0(VarHandle ob, Object obb, long base, $type$ value) {
static void set(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
#if[floatingPoint]
UNSAFE.put$RawType$Unaligned(
SCOPED_MEMORY_ACCESS.put$RawType$Unaligned(bb.scope(),
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
$Type$.$type$ToRaw$RawType$Bits(value),
handle.be);
#else[floatingPoint]
#if[byte]
UNSAFE.put$Type$(
SCOPED_MEMORY_ACCESS.put$Type$(bb.scope(),
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
#else[byte]
UNSAFE.put$Type$Unaligned(
SCOPED_MEMORY_ACCESS.put$Type$Unaligned(bb.scope(),
bb.unsafeGetBase(),
offsetNoVMAlignCheck(bb, base, handle.alignmentMask),
offsetNoVMAlignCheck(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value,
handle.be);
#end[byte]
@ -143,234 +179,234 @@ final class MemoryAccessVarHandle$Type$Helper {
}
@ForceInline
static $type$ getVolatile0(VarHandle ob, Object obb, long base) {
static $type$ getVolatile(VarHandle ob, Object obb, long base) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Volatile(
SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask)));
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask)));
}
@ForceInline
static void setVolatile0(VarHandle ob, Object obb, long base, $type$ value) {
static void setVolatile(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Volatile(
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
SCOPED_MEMORY_ACCESS.put$RawType$Volatile(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, value));
}
@ForceInline
static $type$ getAcquire0(VarHandle ob, Object obb, long base) {
static $type$ getAcquire(VarHandle ob, Object obb, long base) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Acquire(
SCOPED_MEMORY_ACCESS.get$RawType$Acquire(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask)));
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask)));
}
@ForceInline
static void setRelease0(VarHandle ob, Object obb, long base, $type$ value) {
static void setRelease(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Release(
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
SCOPED_MEMORY_ACCESS.put$RawType$Release(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, value));
}
@ForceInline
static $type$ getOpaque0(VarHandle ob, Object obb, long base) {
static $type$ getOpaque(VarHandle ob, Object obb, long base) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, true);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, true);
return convEndian(handle.be,
UNSAFE.get$RawType$Opaque(
SCOPED_MEMORY_ACCESS.get$RawType$Opaque(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask)));
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask)));
}
@ForceInline
static void setOpaque0(VarHandle ob, Object obb, long base, $type$ value) {
static void setOpaque(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
UNSAFE.put$RawType$Opaque(
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
SCOPED_MEMORY_ACCESS.put$RawType$Opaque(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, value));
}
#if[CAS]
@ForceInline
static boolean compareAndSet0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
static boolean compareAndSet(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.compareAndSet$RawType$(
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return SCOPED_MEMORY_ACCESS.compareAndSet$RawType$(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static $type$ compareAndExchange0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
static $type$ compareAndExchange(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$(
SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value)));
}
@ForceInline
static $type$ compareAndExchangeAcquire0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
static $type$ compareAndExchangeAcquire(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Acquire(
SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Acquire(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value)));
}
@ForceInline
static $type$ compareAndExchangeRelease0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
static $type$ compareAndExchangeRelease(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.compareAndExchange$RawType$Release(
SCOPED_MEMORY_ACCESS.compareAndExchange$RawType$Release(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value)));
}
@ForceInline
static boolean weakCompareAndSetPlain0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
static boolean weakCompareAndSetPlain(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Plain(
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Plain(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static boolean weakCompareAndSet0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
static boolean weakCompareAndSet(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$(
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static boolean weakCompareAndSetAcquire0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
static boolean weakCompareAndSetAcquire(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Acquire(
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Acquire(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static boolean weakCompareAndSetRelease0(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
static boolean weakCompareAndSetRelease(VarHandle ob, Object obb, long base, $type$ expected, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
return UNSAFE.weakCompareAndSet$RawType$Release(
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$Release(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, expected), convEndian(handle.be, value));
}
@ForceInline
static $type$ getAndSet0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndSet(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$(
SCOPED_MEMORY_ACCESS.getAndSet$RawType$(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, value)));
}
@ForceInline
static $type$ getAndSetAcquire0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndSetAcquire(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Acquire(
SCOPED_MEMORY_ACCESS.getAndSet$RawType$Acquire(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, value)));
}
@ForceInline
static $type$ getAndSetRelease0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndSetRelease(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
return convEndian(handle.be,
UNSAFE.getAndSet$RawType$Release(
SCOPED_MEMORY_ACCESS.getAndSet$RawType$Release(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
convEndian(handle.be, value)));
}
#end[CAS]
#if[AtomicAdd]
@ForceInline
static $type$ getAndAdd0(VarHandle ob, Object obb, long base, $type$ delta) {
static $type$ getAndAdd(VarHandle ob, Object obb, long base, $type$ delta) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$(
return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
delta);
} else {
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
return getAndAddConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), delta);
}
}
@ForceInline
static $type$ getAndAddAcquire0(VarHandle ob, Object obb, long base, $type$ delta) {
static $type$ getAndAddAcquire(VarHandle ob, Object obb, long base, $type$ delta) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Acquire(
return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Acquire(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
delta);
} else {
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
return getAndAddConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), delta);
}
}
@ForceInline
static $type$ getAndAddRelease0(VarHandle ob, Object obb, long base, $type$ delta) {
static $type$ getAndAddRelease(VarHandle ob, Object obb, long base, $type$ delta) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndAdd$RawType$Release(
return SCOPED_MEMORY_ACCESS.getAndAdd$RawType$Release(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
delta);
} else {
return getAndAddConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), delta);
return getAndAddConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), delta);
}
}
@ForceInline
static $type$ getAndAddConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ delta) {
static $type$ getAndAddConvEndianWithCAS(MemorySegmentProxy bb, long offset, $type$ delta) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(),base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
} while (!SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(),base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue + delta)));
return expectedValue;
}
@ -378,164 +414,164 @@ final class MemoryAccessVarHandle$Type$Helper {
#if[Bitwise]
@ForceInline
static $type$ getAndBitwiseOr0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseOr(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$(
return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
return getAndBitwiseOrConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseOrRelease0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseOrRelease(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Release(
return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Release(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
return getAndBitwiseOrConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseOrAcquire0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseOrAcquire(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseOr$RawType$Acquire(
return SCOPED_MEMORY_ACCESS.getAndBitwiseOr$RawType$Acquire(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseOrConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
return getAndBitwiseOrConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseOrConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
static $type$ getAndBitwiseOrConvEndianWithCAS(MemorySegmentProxy bb, long offset, $type$ value) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(),base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
} while (!SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(),base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue | value)));
return expectedValue;
}
@ForceInline
static $type$ getAndBitwiseAnd0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseAnd(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$(
return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
return getAndBitwiseAndConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseAndRelease0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseAndRelease(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Release(
return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Release(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
return getAndBitwiseAndConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseAndAcquire0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseAndAcquire(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseAnd$RawType$Acquire(
return SCOPED_MEMORY_ACCESS.getAndBitwiseAnd$RawType$Acquire(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseAndConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
return getAndBitwiseAndConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseAndConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
static $type$ getAndBitwiseAndConvEndianWithCAS(MemorySegmentProxy bb, long offset, $type$ value) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(),base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
} while (!SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(),base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue & value)));
return expectedValue;
}
@ForceInline
static $type$ getAndBitwiseXor0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseXor(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$(
return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
return getAndBitwiseXorConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseXorRelease0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseXorRelease(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Release(
return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Release(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
return getAndBitwiseXorConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseXorAcquire0(VarHandle ob, Object obb, long base, $type$ value) {
static $type$ getAndBitwiseXorAcquire(VarHandle ob, Object obb, long base, $type$ value) {
MemoryAccessVarHandleBase handle = (MemoryAccessVarHandleBase)ob;
MemoryAddressProxy bb = checkAddress(obb, base, handle.length, false);
MemorySegmentProxy bb = checkAddress(obb, base, handle.length, false);
if (handle.be == BE) {
return UNSAFE.getAndBitwiseXor$RawType$Acquire(
return SCOPED_MEMORY_ACCESS.getAndBitwiseXor$RawType$Acquire(bb.scope(),
bb.unsafeGetBase(),
offset(bb, base, handle.alignmentMask),
offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask),
value);
} else {
return getAndBitwiseXorConvEndianWithCAS(bb, offset(bb, base, handle.alignmentMask), value);
return getAndBitwiseXorConvEndianWithCAS(bb, offset(handle.skipAlignmentMaskCheck, bb, base, handle.alignmentMask), value);
}
}
@ForceInline
static $type$ getAndBitwiseXorConvEndianWithCAS(MemoryAddressProxy bb, long offset, $type$ value) {
static $type$ getAndBitwiseXorConvEndianWithCAS(MemorySegmentProxy bb, long offset, $type$ value) {
$type$ nativeExpectedValue, expectedValue;
Object base = bb.unsafeGetBase();
do {
nativeExpectedValue = UNSAFE.get$RawType$Volatile(base, offset);
nativeExpectedValue = SCOPED_MEMORY_ACCESS.get$RawType$Volatile(bb.scope(),base, offset);
expectedValue = $RawBoxType$.reverseBytes(nativeExpectedValue);
} while (!UNSAFE.weakCompareAndSet$RawType$(base, offset,
} while (!SCOPED_MEMORY_ACCESS.weakCompareAndSet$RawType$(bb.scope(),base, offset,
nativeExpectedValue, $RawBoxType$.reverseBytes(expectedValue ^ value)));
return expectedValue;
}

View File

@ -106,7 +106,7 @@ class Bits { // package-private
// These methods should be called whenever direct memory is allocated or
// freed. They allow the user to control the amount of direct memory
// which a process may access. All sizes are specified in bytes.
static void reserveMemory(long size, int cap) {
static void reserveMemory(long size, long cap) {
if (!MEMORY_LIMIT_SET && VM.initLevel() >= 1) {
MAX_MEMORY = VM.maxDirectMemory();
@ -185,7 +185,7 @@ class Bits { // package-private
}
}
private static boolean tryReserveMemory(long size, int cap) {
private static boolean tryReserveMemory(long size, long cap) {
// -XX:MaxDirectMemorySize limits the total capacity rather than the
// actual memory usage, which will differ when buffers are page
@ -203,7 +203,7 @@ class Bits { // package-private
}
static void unreserveMemory(long size, int cap) {
static void unreserveMemory(long size, long cap) {
long cnt = COUNT.decrementAndGet();
long reservedMem = RESERVED_MEMORY.addAndGet(-size);
long totalCap = TOTAL_CAPACITY.addAndGet(-cap);

View File

@ -29,6 +29,7 @@ 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.ScopedMemoryAccess;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM.BufferPool;
import jdk.internal.vm.annotation.ForceInline;
@ -193,6 +194,8 @@ public abstract class Buffer {
// Cached unsafe-access object
static final Unsafe UNSAFE = Unsafe.getUnsafe();
static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
/**
* The characteristics of Spliterators that traverse and split elements
* maintained in Buffers.
@ -754,9 +757,18 @@ public abstract class Buffer {
}
@ForceInline
final void checkSegment() {
final ScopedMemoryAccess.Scope scope() {
if (segment != null) {
segment.checkValidState();
return segment.scope();
} else {
return null;
}
}
final void checkScope() {
ScopedMemoryAccess.Scope scope = scope();
if (scope != null) {
scope.checkValidState();
}
}
@ -827,6 +839,21 @@ public abstract class Buffer {
public boolean isLoaded(long address, boolean isSync, long size) {
return MappedMemoryUtils.isLoaded(address, isSync, size);
}
@Override
public void reserveMemory(long size, long cap) {
Bits.reserveMemory(size, cap);
}
@Override
public void unreserveMemory(long size, long cap) {
Bits.unreserveMemory(size, cap);
}
@Override
public int pageSize() {
return Bits.pageSize();
}
});
}

View File

@ -24,6 +24,7 @@
*/
package java.nio;
import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.util.ArraysSupport;
/**
@ -31,12 +32,14 @@ import jdk.internal.util.ArraysSupport;
*/
final class BufferMismatch {
final static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
static int mismatch(ByteBuffer a, int aOff, ByteBuffer b, int bOff, int length) {
int i = 0;
if (length > 7) {
if (a.get(aOff) != b.get(bOff))
return 0;
i = ArraysSupport.vectorizedMismatch(
i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(),
a.base(), a.address + aOff,
b.base(), b.address + bOff,
length,
@ -60,7 +63,7 @@ final class BufferMismatch {
&& a.charRegionOrder() != null && b.charRegionOrder() != null) {
if (a.get(aOff) != b.get(bOff))
return 0;
i = ArraysSupport.vectorizedMismatch(
i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(),
a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_CHAR_INDEX_SCALE),
b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_CHAR_INDEX_SCALE),
length,
@ -80,7 +83,7 @@ final class BufferMismatch {
if (length > 3 && a.order() == b.order()) {
if (a.get(aOff) != b.get(bOff))
return 0;
i = ArraysSupport.vectorizedMismatch(
i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(),
a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_SHORT_INDEX_SCALE),
b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_SHORT_INDEX_SCALE),
length,
@ -100,7 +103,7 @@ final class BufferMismatch {
if (length > 1 && a.order() == b.order()) {
if (a.get(aOff) != b.get(bOff))
return 0;
i = ArraysSupport.vectorizedMismatch(
i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(),
a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_INT_INDEX_SCALE),
b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_INT_INDEX_SCALE),
length,
@ -119,7 +122,7 @@ final class BufferMismatch {
int i = 0;
if (length > 1 && a.order() == b.order()) {
if (Float.floatToRawIntBits(a.get(aOff)) == Float.floatToRawIntBits(b.get(bOff))) {
i = ArraysSupport.vectorizedMismatch(
i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(),
a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_FLOAT_INDEX_SCALE),
b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_FLOAT_INDEX_SCALE),
length,
@ -158,7 +161,7 @@ final class BufferMismatch {
if (length > 0 && a.order() == b.order()) {
if (a.get(aOff) != b.get(bOff))
return 0;
i = ArraysSupport.vectorizedMismatch(
i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(),
a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_LONG_INDEX_SCALE),
b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_LONG_INDEX_SCALE),
length,
@ -176,7 +179,7 @@ final class BufferMismatch {
int i = 0;
if (length > 0 && a.order() == b.order()) {
if (Double.doubleToRawLongBits(a.get(aOff)) == Double.doubleToRawLongBits(b.get(bOff))) {
i = ArraysSupport.vectorizedMismatch(
i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(a.scope(), b.scope(),
a.base(), a.address + (aOff << ArraysSupport.LOG2_ARRAY_DOUBLE_INDEX_SCALE),
b.base(), b.address + (bOff << ArraysSupport.LOG2_ARRAY_DOUBLE_INDEX_SCALE),
length,

View File

@ -130,22 +130,20 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
}
public $type$ get() {
checkSegment();
$memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(nextGetIndex()),
$memtype$ x = SCOPED_MEMORY_ACCESS.get$Memtype$Unaligned(scope(), bb.hb, byteOffset(nextGetIndex()),
{#if[boB]?true:false});
return $fromBits$(x);
}
public $type$ get(int i) {
checkSegment();
$memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)),
$memtype$ x = SCOPED_MEMORY_ACCESS.get$Memtype$Unaligned(scope(), bb.hb, byteOffset(checkIndex(i)),
{#if[boB]?true:false});
return $fromBits$(x);
}
#if[streamableType]
$type$ getUnchecked(int i) {
$memtype$ x = UNSAFE.get$Memtype$Unaligned(bb.hb, byteOffset(i),
$memtype$ x = SCOPED_MEMORY_ACCESS.get$Memtype$Unaligned(null, bb.hb, byteOffset(i),
{#if[boB]?true:false});
return $fromBits$(x);
}
@ -155,9 +153,8 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
public $Type$Buffer put($type$ x) {
#if[rw]
checkSegment();
$memtype$ y = $toBits$(x);
UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(nextPutIndex()), y,
SCOPED_MEMORY_ACCESS.put$Memtype$Unaligned(scope(), bb.hb, byteOffset(nextPutIndex()), y,
{#if[boB]?true:false});
return this;
#else[rw]
@ -167,9 +164,8 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
public $Type$Buffer put(int i, $type$ x) {
#if[rw]
checkSegment();
$memtype$ y = $toBits$(x);
UNSAFE.put$Memtype$Unaligned(bb.hb, byteOffset(checkIndex(i)), y,
SCOPED_MEMORY_ACCESS.put$Memtype$Unaligned(scope(), bb.hb, byteOffset(checkIndex(i)), y,
{#if[boB]?true:false});
return this;
#else[rw]

View File

@ -33,8 +33,7 @@ class XXX {
private $type$ get$Type$(long a) {
try {
checkSegment();
$memtype$ x = UNSAFE.get$Memtype$Unaligned(null, a, bigEndian);
$memtype$ x = SCOPED_MEMORY_ACCESS.get$Memtype$Unaligned(scope(), null, a, bigEndian);
return $fromBits$(x);
} finally {
Reference.reachabilityFence(this);
@ -62,9 +61,8 @@ class XXX {
private ByteBuffer put$Type$(long a, $type$ x) {
#if[rw]
try {
checkSegment();
$memtype$ y = $toBits$(x);
UNSAFE.put$Memtype$Unaligned(null, a, y, bigEndian);
SCOPED_MEMORY_ACCESS.put$Memtype$Unaligned(scope(), null, a, y, bigEndian);
} finally {
Reference.reachabilityFence(this);
}

View File

@ -31,6 +31,7 @@ import java.io.FileDescriptor;
import java.lang.ref.Reference;
import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.ScopedMemoryAccess.Scope;
import jdk.internal.misc.VM;
import jdk.internal.ref.Cleaner;
import sun.nio.ch.DirectBuffer;
@ -264,6 +265,17 @@ class Direct$Type$Buffer$RW$$BO$
#if[rw]
public long address() {
Scope scope = scope();
if (scope != null) {
if (scope.ownerThread() == null) {
throw new UnsupportedOperationException("ByteBuffer derived from shared segments not supported");
}
try {
scope.checkValidState();
} catch (Scope.ScopedAccessError e) {
throw new IllegalStateException("This segment is already closed");
}
}
return address;
}
@ -273,8 +285,7 @@ class Direct$Type$Buffer$RW$$BO$
public $type$ get() {
try {
checkSegment();
return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(nextGetIndex()))));
return $fromBits$($swap$(SCOPED_MEMORY_ACCESS.get$Swaptype$(scope(), null, ix(nextGetIndex()))));
} finally {
Reference.reachabilityFence(this);
}
@ -282,8 +293,7 @@ class Direct$Type$Buffer$RW$$BO$
public $type$ get(int i) {
try {
checkSegment();
return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(checkIndex(i)))));
return $fromBits$($swap$(SCOPED_MEMORY_ACCESS.get$Swaptype$(scope(), null, ix(checkIndex(i)))));
} finally {
Reference.reachabilityFence(this);
}
@ -292,7 +302,7 @@ class Direct$Type$Buffer$RW$$BO$
#if[streamableType]
$type$ getUnchecked(int i) {
try {
return $fromBits$($swap$(UNSAFE.get$Swaptype$(ix(i))));
return $fromBits$($swap$(SCOPED_MEMORY_ACCESS.get$Swaptype$(null, null, ix(i))));
} finally {
Reference.reachabilityFence(this);
}
@ -301,7 +311,6 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer get($type$[] dst, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(offset, length, dst.length);
int pos = position();
@ -315,7 +324,7 @@ class Direct$Type$Buffer$RW$$BO$
try {
#if[!byte]
if (order() != ByteOrder.nativeOrder())
UNSAFE.copySwapMemory(null,
SCOPED_MEMORY_ACCESS.copySwapMemory(scope(), null, null,
ix(pos),
dst,
dstOffset,
@ -323,7 +332,7 @@ class Direct$Type$Buffer$RW$$BO$
(long)1 << $LG_BYTES_PER_VALUE$);
else
#end[!byte]
UNSAFE.copyMemory(null,
SCOPED_MEMORY_ACCESS.copyMemory(scope(), null, null,
ix(pos),
dst,
dstOffset,
@ -343,7 +352,6 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer get(int index, $type$[] dst, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, dst.length);
@ -352,7 +360,7 @@ class Direct$Type$Buffer$RW$$BO$
try {
#if[!byte]
if (order() != ByteOrder.nativeOrder())
UNSAFE.copySwapMemory(null,
SCOPED_MEMORY_ACCESS.copySwapMemory(scope(), null, null,
ix(index),
dst,
dstOffset,
@ -360,7 +368,7 @@ class Direct$Type$Buffer$RW$$BO$
(long)1 << $LG_BYTES_PER_VALUE$);
else
#end[!byte]
UNSAFE.copyMemory(null,
SCOPED_MEMORY_ACCESS.copyMemory(scope(), null, null,
ix(index),
dst,
dstOffset,
@ -381,8 +389,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put($type$ x) {
#if[rw]
try {
checkSegment();
UNSAFE.put$Swaptype$(ix(nextPutIndex()), $swap$($toBits$(x)));
SCOPED_MEMORY_ACCESS.put$Swaptype$(scope(), null, ix(nextPutIndex()), $swap$($toBits$(x)));
} finally {
Reference.reachabilityFence(this);
}
@ -395,8 +402,7 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put(int i, $type$ x) {
#if[rw]
try {
checkSegment();
UNSAFE.put$Swaptype$(ix(checkIndex(i)), $swap$($toBits$(x)));
SCOPED_MEMORY_ACCESS.put$Swaptype$(scope(), null, ix(checkIndex(i)), $swap$($toBits$(x)));
} finally {
Reference.reachabilityFence(this);
}
@ -408,7 +414,6 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put($Type$Buffer src) {
#if[rw]
checkSegment();
super.put(src);
return this;
#else[rw]
@ -418,7 +423,6 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) {
#if[rw]
checkSegment();
super.put(index, src, offset, length);
return this;
#else[rw]
@ -428,7 +432,6 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put($type$[] src, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(offset, length, src.length);
int pos = position();
@ -442,7 +445,7 @@ class Direct$Type$Buffer$RW$$BO$
try {
#if[!byte]
if (order() != ByteOrder.nativeOrder())
UNSAFE.copySwapMemory(src,
SCOPED_MEMORY_ACCESS.copySwapMemory(scope(), null, src,
srcOffset,
null,
ix(pos),
@ -450,7 +453,7 @@ class Direct$Type$Buffer$RW$$BO$
(long)1 << $LG_BYTES_PER_VALUE$);
else
#end[!byte]
UNSAFE.copyMemory(src,
SCOPED_MEMORY_ACCESS.copyMemory(scope(), null, src,
srcOffset,
null,
ix(pos),
@ -470,7 +473,6 @@ class Direct$Type$Buffer$RW$$BO$
public $Type$Buffer put(int index, $type$[] src, int offset, int length) {
#if[rw]
checkSegment();
if (((long)length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_FROM_ARRAY_THRESHOLD) {
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, src.length);
@ -480,7 +482,7 @@ class Direct$Type$Buffer$RW$$BO$
try {
#if[!byte]
if (order() != ByteOrder.nativeOrder())
UNSAFE.copySwapMemory(src,
SCOPED_MEMORY_ACCESS.copySwapMemory(scope(), null, src,
srcOffset,
null,
ix(index),
@ -488,11 +490,9 @@ class Direct$Type$Buffer$RW$$BO$
(long)1 << $LG_BYTES_PER_VALUE$);
else
#end[!byte]
UNSAFE.copyMemory(src,
srcOffset,
null,
ix(index),
(long)length << $LG_BYTES_PER_VALUE$);
SCOPED_MEMORY_ACCESS.copyMemory(
scope(), null, src,
srcOffset, null, ix(index), (long)length << $LG_BYTES_PER_VALUE$);
} finally {
Reference.reachabilityFence(this);
}
@ -512,7 +512,8 @@ class Direct$Type$Buffer$RW$$BO$
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
try {
UNSAFE.copyMemory(ix(pos), ix(0), (long)rem << $LG_BYTES_PER_VALUE$);
SCOPED_MEMORY_ACCESS.copyMemory(scope(), null, null,
ix(pos), null, ix(0), (long)rem << $LG_BYTES_PER_VALUE$);
} finally {
Reference.reachabilityFence(this);
}

View File

@ -162,12 +162,10 @@ class Heap$Type$Buffer$RW$
#end[byte]
public $type$ get() {
checkSegment();
return hb[ix(nextGetIndex())];
}
public $type$ get(int i) {
checkSegment();
return hb[ix(checkIndex(i))];
}
@ -178,7 +176,7 @@ class Heap$Type$Buffer$RW$
#end[streamableType]
public $Type$Buffer get($type$[] dst, int offset, int length) {
checkSegment();
checkScope();
Objects.checkFromIndexSize(offset, length, dst.length);
int pos = position();
if (length > limit() - pos)
@ -189,7 +187,7 @@ class Heap$Type$Buffer$RW$
}
public $Type$Buffer get(int index, $type$[] dst, int offset, int length) {
checkSegment();
checkScope();
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, dst.length);
System.arraycopy(hb, ix(index), dst, offset, length);
@ -208,7 +206,6 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put($type$ x) {
#if[rw]
checkSegment();
hb[ix(nextPutIndex())] = x;
return this;
#else[rw]
@ -218,7 +215,6 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put(int i, $type$ x) {
#if[rw]
checkSegment();
hb[ix(checkIndex(i))] = x;
return this;
#else[rw]
@ -228,7 +224,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put($type$[] src, int offset, int length) {
#if[rw]
checkSegment();
checkScope();
Objects.checkFromIndexSize(offset, length, src.length);
int pos = position();
if (length > limit() - pos)
@ -243,7 +239,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put($Type$Buffer src) {
#if[rw]
checkSegment();
checkScope();
super.put(src);
return this;
#else[rw]
@ -253,7 +249,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) {
#if[rw]
checkSegment();
checkScope();
super.put(index, src, offset, length);
return this;
#else[rw]
@ -263,7 +259,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer put(int index, $type$[] src, int offset, int length) {
#if[rw]
checkSegment();
checkScope();
Objects.checkFromIndexSize(index, length, limit());
Objects.checkFromIndexSize(offset, length, src.length);
System.arraycopy(src, offset, hb, ix(index), length);
@ -276,7 +272,7 @@ class Heap$Type$Buffer$RW$
#if[char]
public $Type$Buffer put(String src, int start, int end) {
checkSegment();
checkScope();
int length = end - start;
Objects.checkFromIndexSize(start, length, src.length());
if (isReadOnly())
@ -328,20 +324,18 @@ class Heap$Type$Buffer$RW$
#if[rw]
public char getChar() {
checkSegment();
return UNSAFE.getCharUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
return SCOPED_MEMORY_ACCESS.getCharUnaligned(scope(), hb, byteOffset(nextGetIndex(2)), bigEndian);
}
public char getChar(int i) {
return UNSAFE.getCharUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian);
return SCOPED_MEMORY_ACCESS.getCharUnaligned(scope(), hb, byteOffset(checkIndex(i, 2)), bigEndian);
}
#end[rw]
public $Type$Buffer putChar(char x) {
#if[rw]
checkSegment();
UNSAFE.putCharUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
SCOPED_MEMORY_ACCESS.putCharUnaligned(scope(), hb, byteOffset(nextPutIndex(2)), x, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -350,8 +344,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putChar(int i, char x) {
#if[rw]
checkSegment();
UNSAFE.putCharUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
SCOPED_MEMORY_ACCESS.putCharUnaligned(scope(), hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -383,21 +376,18 @@ class Heap$Type$Buffer$RW$
#if[rw]
public short getShort() {
checkSegment();
return UNSAFE.getShortUnaligned(hb, byteOffset(nextGetIndex(2)), bigEndian);
return SCOPED_MEMORY_ACCESS.getShortUnaligned(scope(), hb, byteOffset(nextGetIndex(2)), bigEndian);
}
public short getShort(int i) {
checkSegment();
return UNSAFE.getShortUnaligned(hb, byteOffset(checkIndex(i, 2)), bigEndian);
return SCOPED_MEMORY_ACCESS.getShortUnaligned(scope(), hb, byteOffset(checkIndex(i, 2)), bigEndian);
}
#end[rw]
public $Type$Buffer putShort(short x) {
#if[rw]
checkSegment();
UNSAFE.putShortUnaligned(hb, byteOffset(nextPutIndex(2)), x, bigEndian);
SCOPED_MEMORY_ACCESS.putShortUnaligned(scope(), hb, byteOffset(nextPutIndex(2)), x, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -406,8 +396,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putShort(int i, short x) {
#if[rw]
checkSegment();
UNSAFE.putShortUnaligned(hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
SCOPED_MEMORY_ACCESS.putShortUnaligned(scope(), hb, byteOffset(checkIndex(i, 2)), x, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -439,21 +428,18 @@ class Heap$Type$Buffer$RW$
#if[rw]
public int getInt() {
checkSegment();
return UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
return SCOPED_MEMORY_ACCESS.getIntUnaligned(scope(), hb, byteOffset(nextGetIndex(4)), bigEndian);
}
public int getInt(int i) {
checkSegment();
return UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
return SCOPED_MEMORY_ACCESS.getIntUnaligned(scope(), hb, byteOffset(checkIndex(i, 4)), bigEndian);
}
#end[rw]
public $Type$Buffer putInt(int x) {
#if[rw]
checkSegment();
UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), x, bigEndian);
SCOPED_MEMORY_ACCESS.putIntUnaligned(scope(), hb, byteOffset(nextPutIndex(4)), x, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -462,8 +448,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putInt(int i, int x) {
#if[rw]
checkSegment();
UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), x, bigEndian);
SCOPED_MEMORY_ACCESS.putIntUnaligned(scope(), hb, byteOffset(checkIndex(i, 4)), x, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -495,21 +480,18 @@ class Heap$Type$Buffer$RW$
#if[rw]
public long getLong() {
checkSegment();
return UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
return SCOPED_MEMORY_ACCESS.getLongUnaligned(scope(), hb, byteOffset(nextGetIndex(8)), bigEndian);
}
public long getLong(int i) {
checkSegment();
return UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
return SCOPED_MEMORY_ACCESS.getLongUnaligned(scope(), hb, byteOffset(checkIndex(i, 8)), bigEndian);
}
#end[rw]
public $Type$Buffer putLong(long x) {
#if[rw]
checkSegment();
UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), x, bigEndian);
SCOPED_MEMORY_ACCESS.putLongUnaligned(scope(), hb, byteOffset(nextPutIndex(8)), x, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -518,8 +500,7 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putLong(int i, long x) {
#if[rw]
checkSegment();
UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), x, bigEndian);
SCOPED_MEMORY_ACCESS.putLongUnaligned(scope(), hb, byteOffset(checkIndex(i, 8)), x, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -551,14 +532,12 @@ class Heap$Type$Buffer$RW$
#if[rw]
public float getFloat() {
checkSegment();
int x = UNSAFE.getIntUnaligned(hb, byteOffset(nextGetIndex(4)), bigEndian);
int x = SCOPED_MEMORY_ACCESS.getIntUnaligned(scope(), hb, byteOffset(nextGetIndex(4)), bigEndian);
return Float.intBitsToFloat(x);
}
public float getFloat(int i) {
checkSegment();
int x = UNSAFE.getIntUnaligned(hb, byteOffset(checkIndex(i, 4)), bigEndian);
int x = SCOPED_MEMORY_ACCESS.getIntUnaligned(scope(), hb, byteOffset(checkIndex(i, 4)), bigEndian);
return Float.intBitsToFloat(x);
}
@ -566,9 +545,8 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putFloat(float x) {
#if[rw]
checkSegment();
int y = Float.floatToRawIntBits(x);
UNSAFE.putIntUnaligned(hb, byteOffset(nextPutIndex(4)), y, bigEndian);
SCOPED_MEMORY_ACCESS.putIntUnaligned(scope(), hb, byteOffset(nextPutIndex(4)), y, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -577,9 +555,8 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putFloat(int i, float x) {
#if[rw]
checkSegment();
int y = Float.floatToRawIntBits(x);
UNSAFE.putIntUnaligned(hb, byteOffset(checkIndex(i, 4)), y, bigEndian);
SCOPED_MEMORY_ACCESS.putIntUnaligned(scope(), hb, byteOffset(checkIndex(i, 4)), y, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -611,14 +588,12 @@ class Heap$Type$Buffer$RW$
#if[rw]
public double getDouble() {
checkSegment();
long x = UNSAFE.getLongUnaligned(hb, byteOffset(nextGetIndex(8)), bigEndian);
long x = SCOPED_MEMORY_ACCESS.getLongUnaligned(scope(), hb, byteOffset(nextGetIndex(8)), bigEndian);
return Double.longBitsToDouble(x);
}
public double getDouble(int i) {
checkSegment();
long x = UNSAFE.getLongUnaligned(hb, byteOffset(checkIndex(i, 8)), bigEndian);
long x = SCOPED_MEMORY_ACCESS.getLongUnaligned(scope(), hb, byteOffset(checkIndex(i, 8)), bigEndian);
return Double.longBitsToDouble(x);
}
@ -626,9 +601,8 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putDouble(double x) {
#if[rw]
checkSegment();
long y = Double.doubleToRawLongBits(x);
UNSAFE.putLongUnaligned(hb, byteOffset(nextPutIndex(8)), y, bigEndian);
SCOPED_MEMORY_ACCESS.putLongUnaligned(scope(), hb, byteOffset(nextPutIndex(8)), y, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();
@ -637,9 +611,8 @@ class Heap$Type$Buffer$RW$
public $Type$Buffer putDouble(int i, double x) {
#if[rw]
checkSegment();
long y = Double.doubleToRawLongBits(x);
UNSAFE.putLongUnaligned(hb, byteOffset(checkIndex(i, 8)), y, bigEndian);
SCOPED_MEMORY_ACCESS.putLongUnaligned(scope(), hb, byteOffset(checkIndex(i, 8)), y, bigEndian);
return this;
#else[rw]
throw new ReadOnlyBufferException();

View File

@ -31,6 +31,7 @@ import java.util.Objects;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.ScopedMemoryAccess;
/**
@ -87,6 +88,8 @@ public abstract class MappedByteBuffer
// determines the behavior of force operations.
private final boolean isSync;
static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
// This should only be invoked by the DirectByteBuffer constructors
//
MappedByteBuffer(int mark, int pos, int lim, int cap, // package-private
@ -173,7 +176,7 @@ public abstract class MappedByteBuffer
if (fd == null) {
return true;
}
return MappedMemoryUtils.isLoaded(address, isSync, capacity());
return SCOPED_MEMORY_ACCESS.isLoaded(scope(), address, isSync, capacity());
}
/**
@ -191,7 +194,7 @@ public abstract class MappedByteBuffer
return this;
}
try {
MappedMemoryUtils.load(address, isSync, capacity());
SCOPED_MEMORY_ACCESS.load(scope(), address, isSync, capacity());
} finally {
Reference.reachabilityFence(this);
}
@ -280,7 +283,7 @@ public abstract class MappedByteBuffer
if ((address != 0) && (limit != 0)) {
// check inputs
Objects.checkFromIndexSize(index, length, limit);
MappedMemoryUtils.force(fd, address, isSync, index, length);
SCOPED_MEMORY_ACCESS.force(scope(), fd, address, isSync, index, length);
}
return this;
}

View File

@ -1045,11 +1045,9 @@ public abstract class $Type$Buffer
if (this.order() == src.order()) {
#end[!byte]
try {
UNSAFE.copyMemory(srcBase,
srcAddr,
base,
addr,
len);
SCOPED_MEMORY_ACCESS.copyMemory(
scope(), src.scope(), srcBase,
srcAddr, base, addr, len);
} finally {
Reference.reachabilityFence(src);
Reference.reachabilityFence(this);
@ -1057,12 +1055,9 @@ public abstract class $Type$Buffer
#if[!byte]
} else {
try {
UNSAFE.copySwapMemory(srcBase,
srcAddr,
base,
addr,
len,
(long)1 << $LG_BYTES_PER_VALUE$);
SCOPED_MEMORY_ACCESS.copySwapMemory(
scope(), src.scope(), srcBase,
srcAddr, base, addr, len, (long)1 << $LG_BYTES_PER_VALUE$);
} finally {
Reference.reachabilityFence(src);
Reference.reachabilityFence(this);

View File

@ -81,44 +81,8 @@ public interface JavaLangInvokeAccess {
* Used by {@code jdk.internal.foreign.LayoutPath} and
* {@code jdk.incubator.foreign.MemoryHandles}.
*/
VarHandle memoryAccessVarHandle(Class<?> carrier, long alignmentMask,
ByteOrder order, long offset, long[] strides);
/**
* Is {@code handle} a memory access varhandle?
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
boolean isMemoryAccessVarHandle(VarHandle handle);
/**
* Returns the carrier associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
Class<?> memoryAddressCarrier(VarHandle handle);
/**
* Returns the alignment mask associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long memoryAddressAlignmentMask(VarHandle handle);
/**
* Returns the byte order associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
ByteOrder memoryAddressByteOrder(VarHandle handle);
/**
* Returns the offset associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long memoryAddressOffset(VarHandle handle);
/**
* Returns the strides associated with a memory access var handle.
* Used by {@code jdk.incubator.foreign.MemoryHandles}.
*/
long[] memoryAddressStrides(VarHandle handle);
VarHandle memoryAccessVarHandle(Class<?> carrier, boolean skipAlignmentMaskCheck, long alignmentMask,
ByteOrder order);
/**
* Var handle carrier combinator.

View File

@ -104,4 +104,19 @@ public interface JavaNioAccess {
* Used by {@code jdk.internal.foreign.MappedMemorySegmentImpl} and byte buffer var handle views.
*/
boolean isLoaded(long address, boolean isSync, long size);
/**
* Used by {@code jdk.internal.foreign.NativeMemorySegmentImpl}.
*/
void reserveMemory(long size, long cap);
/**
* Used by {@code jdk.internal.foreign.NativeMemorySegmentImpl}.
*/
void unreserveMemory(long size, long cap);
/**
* Used by {@code jdk.internal.foreign.NativeMemorySegmentImpl}.
*/
int pageSize();
}

View File

@ -26,10 +26,78 @@
package jdk.internal.access.foreign;
import jdk.internal.misc.ScopedMemoryAccess;
/**
* This proxy interface is required to allow instances of the {@code MemorySegment} interface (which is defined inside
* an incubating module) to be accessed from the memory access var handles.
*/
public interface MemorySegmentProxy {
void checkValidState();
/**
* Check that memory access is within spatial bounds and that access is compatible with segment access modes.
* @throws UnsupportedOperationException if underlying segment has incompatible access modes (e.g. attempting to write
* a read-only segment).
* @throws IndexOutOfBoundsException if access is out-of-bounds.
*/
void checkAccess(long offset, long length, boolean readOnly);
long unsafeGetOffset();
Object unsafeGetBase();
boolean isSmall();
ScopedMemoryAccess.Scope scope();
/* Helper functions for offset computations. These are required so that we can avoid issuing long opcodes
* (e.g. LMUL, LADD) when we're operating on 'small' segments (segments whose length can be expressed with an int).
* C2 BCE code is very sensitive to the kind of opcode being emitted, and this workaround allows us to rescue
* BCE when working with small segments. This workaround should be dropped when JDK-8223051 is resolved.
*/
static long addOffsets(long op1, long op2, MemorySegmentProxy segmentProxy) {
if (segmentProxy.isSmall()) {
// force ints for BCE
if (op1 > Integer.MAX_VALUE || op2 > Integer.MAX_VALUE
|| op1 < Integer.MIN_VALUE || op2 < Integer.MIN_VALUE) {
throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
int i1 = (int)op1;
int i2 = (int)op2;
try {
return Math.addExact(i1, i2);
} catch (ArithmeticException ex) {
throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
} else {
try {
return Math.addExact(op1, op2);
} catch (ArithmeticException ex) {
throw overflowException(Long.MIN_VALUE, Long.MAX_VALUE);
}
}
}
static long multiplyOffsets(long op1, long op2, MemorySegmentProxy segmentProxy) {
if (segmentProxy.isSmall()) {
if (op1 > Integer.MAX_VALUE || op2 > Integer.MAX_VALUE
|| op1 < Integer.MIN_VALUE || op2 < Integer.MIN_VALUE) {
throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
// force ints for BCE
int i1 = (int)op1;
int i2 = (int)op2;
try {
return Math.multiplyExact(i1, i2);
} catch (ArithmeticException ex) {
throw overflowException(Integer.MIN_VALUE, Integer.MAX_VALUE);
}
} else {
try {
return Math.multiplyExact(op1, op2);
} catch (ArithmeticException ex) {
throw overflowException(Long.MIN_VALUE, Long.MAX_VALUE);
}
}
}
private static IndexOutOfBoundsException overflowException(long min, long max) {
return new IndexOutOfBoundsException(String.format("Overflow occurred during offset computation ; offset exceeded range { %d .. %d }", min, max));
}
}

View File

@ -0,0 +1,698 @@
@ForceInline
public $type$ get$Type$(Scope scope, Object base, long offset) {
try {
return get$Type$Internal(scope, base, offset);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ get$Type$Internal(Scope scope, Object base, long offset) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.get$Type$(base, offset);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public void put$Type$(Scope scope, Object base, long offset, $type$ value) {
try {
put$Type$Internal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private void put$Type$Internal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
UNSAFE.put$Type$(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
#if[Unaligned]
@ForceInline
public $type$ get$Type$Unaligned(Scope scope, Object base, long offset, boolean be) {
try {
return get$Type$UnalignedInternal(scope, base, offset, be);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ get$Type$UnalignedInternal(Scope scope, Object base, long offset, boolean be) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.get$Type$Unaligned(base, offset, be);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public void put$Type$Unaligned(Scope scope, Object base, long offset, $type$ value, boolean be) {
try {
put$Type$UnalignedInternal(scope, base, offset, value, be);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private void put$Type$UnalignedInternal(Scope scope, Object base, long offset, $type$ value, boolean be) {
try {
if (scope != null) {
scope.checkValidState();
}
UNSAFE.put$Type$Unaligned(base, offset, value, be);
} finally {
Reference.reachabilityFence(scope);
}
}
#end[Unaligned]
@ForceInline
public $type$ get$Type$Volatile(Scope scope, Object base, long offset) {
try {
return get$Type$VolatileInternal(scope, base, offset);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ get$Type$VolatileInternal(Scope scope, Object base, long offset) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.get$Type$Volatile(base, offset);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public void put$Type$Volatile(Scope scope, Object base, long offset, $type$ value) {
try {
put$Type$VolatileInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private void put$Type$VolatileInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
UNSAFE.put$Type$Volatile(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ get$Type$Acquire(Scope scope, Object base, long offset) {
try {
return get$Type$AcquireInternal(scope, base, offset);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ get$Type$AcquireInternal(Scope scope, Object base, long offset) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.get$Type$Acquire(base, offset);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public void put$Type$Release(Scope scope, Object base, long offset, $type$ value) {
try {
put$Type$ReleaseInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private void put$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
UNSAFE.put$Type$Release(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ get$Type$Opaque(Scope scope, Object base, long offset) {
try {
return get$Type$OpaqueInternal(scope, base, offset);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ get$Type$OpaqueInternal(Scope scope, Object base, long offset) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.get$Type$Opaque(base, offset);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public void put$Type$Opaque(Scope scope, Object base, long offset, $type$ value) {
try {
put$Type$OpaqueInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private void put$Type$OpaqueInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
UNSAFE.put$Type$Opaque(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
#if[CAS]
@ForceInline
public boolean compareAndSet$Type$(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
return compareAndSet$Type$Internal(scope, base, offset, expected, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private boolean compareAndSet$Type$Internal(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.compareAndSet$Type$(base, offset, expected, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ compareAndExchange$Type$(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
return compareAndExchange$Type$Internal(scope, base, offset, expected, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ compareAndExchange$Type$Internal(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.compareAndExchange$Type$(base, offset, expected, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ compareAndExchange$Type$Acquire(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
return compareAndExchange$Type$AcquireInternal(scope, base, offset, expected, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ compareAndExchange$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.compareAndExchange$Type$Acquire(base, offset, expected, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ compareAndExchange$Type$Release(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
return compareAndExchange$Type$ReleaseInternal(scope, base, offset, expected, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ compareAndExchange$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.compareAndExchange$Type$Release(base, offset, expected, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public boolean weakCompareAndSet$Type$Plain(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
return weakCompareAndSet$Type$PlainInternal(scope, base, offset, expected, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private boolean weakCompareAndSet$Type$PlainInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.weakCompareAndSet$Type$Plain(base, offset, expected, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public boolean weakCompareAndSet$Type$(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
return weakCompareAndSet$Type$Internal(scope, base, offset, expected, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private boolean weakCompareAndSet$Type$Internal(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.weakCompareAndSet$Type$(base, offset, expected, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public boolean weakCompareAndSet$Type$Acquire(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
return weakCompareAndSet$Type$AcquireInternal(scope, base, offset, expected, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private boolean weakCompareAndSet$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.weakCompareAndSet$Type$Acquire(base, offset, expected, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public boolean weakCompareAndSet$Type$Release(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
return weakCompareAndSet$Type$ReleaseInternal(scope, base, offset, expected, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private boolean weakCompareAndSet$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ expected, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.weakCompareAndSet$Type$Release(base, offset, expected, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndSet$Type$(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndSet$Type$Internal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndSet$Type$Internal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndSet$Type$(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndSet$Type$Acquire(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndSet$Type$AcquireInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndSet$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndSet$Type$Acquire(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndSet$Type$Release(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndSet$Type$ReleaseInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndSet$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndSet$Type$Release(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
#end[CAS]
#if[AtomicAdd]
@ForceInline
public $type$ getAndAdd$Type$(Scope scope, Object base, long offset, $type$ delta) {
try {
return getAndAdd$Type$Internal(scope, base, offset, delta);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndAdd$Type$Internal(Scope scope, Object base, long offset, $type$ delta) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndAdd$Type$(base, offset, delta);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndAdd$Type$Acquire(Scope scope, Object base, long offset, $type$ delta) {
try {
return getAndAdd$Type$AcquireInternal(scope, base, offset, delta);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndAdd$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ delta) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndAdd$Type$Acquire(base, offset, delta);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndAdd$Type$Release(Scope scope, Object base, long offset, $type$ delta) {
try {
return getAndAdd$Type$ReleaseInternal(scope, base, offset, delta);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndAdd$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ delta) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndAdd$Type$Release(base, offset, delta);
} finally {
Reference.reachabilityFence(scope);
}
}
#end[AtomicAdd]
#if[Bitwise]
@ForceInline
public $type$ getAndBitwiseOr$Type$(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndBitwiseOr$Type$Internal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndBitwiseOr$Type$Internal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndBitwiseOr$Type$(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndBitwiseOr$Type$Acquire(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndBitwiseOr$Type$AcquireInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndBitwiseOr$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndBitwiseOr$Type$Acquire(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndBitwiseOr$Type$Release(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndBitwiseOr$Type$ReleaseInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndBitwiseOr$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndBitwiseOr$Type$Release(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndBitwiseAnd$Type$(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndBitwiseAnd$Type$Internal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndBitwiseAnd$Type$Internal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndBitwiseAnd$Type$(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndBitwiseAnd$Type$Acquire(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndBitwiseAnd$Type$AcquireInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndBitwiseAnd$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndBitwiseAnd$Type$Acquire(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndBitwiseAnd$Type$Release(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndBitwiseAnd$Type$ReleaseInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndBitwiseAnd$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndBitwiseAnd$Type$Release(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndBitwiseXor$Type$(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndBitwiseXor$Type$Internal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndBitwiseXor$Type$Internal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndBitwiseXor$Type$(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndBitwiseXor$Type$Acquire(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndBitwiseXor$Type$AcquireInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndBitwiseXor$Type$AcquireInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndBitwiseXor$Type$Acquire(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public $type$ getAndBitwiseXor$Type$Release(Scope scope, Object base, long offset, $type$ value) {
try {
return getAndBitwiseXor$Type$ReleaseInternal(scope, base, offset, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private $type$ getAndBitwiseXor$Type$ReleaseInternal(Scope scope, Object base, long offset, $type$ value) {
try {
if (scope != null) {
scope.checkValidState();
}
return UNSAFE.getAndBitwiseXor$Type$Release(base, offset, value);
} finally {
Reference.reachabilityFence(scope);
}
}
#end[Bitwise]

View File

@ -0,0 +1,327 @@
/*
* 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 jdk.internal.misc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.ref.Reference;
import java.io.FileDescriptor;
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.ArraysSupport;
import jdk.internal.vm.annotation.ForceInline;
/**
* This class defines low-level methods to access on-heap and off-heap memory. The methods in this class
* can be thought of as thin wrappers around methods provided in the {@link Unsafe} class. All the methods in this
* class, accept one or more {@link Scope} parameter, which is used to validate as to whether access to memory
* can be performed in a safe fashion - more specifically, to ensure that the memory being accessed has not
* already been released (which would result in a hard VM crash).
* <p>
* Accessing and releasing memory from a single thread is not problematic - after all, a given thread cannot,
* at the same time, access a memory region <em>and</em> free it. But ensuring correctness of memory access
* when multiple threads are involved is much trickier, as there can be cases where a thread is accessing
* a memory region while another thread is releasing it.
* <p>
* This class provides tools to manage races when multiple threads are accessing and/or releasing the same memory
* region concurrently. More specifically, when a thread wants to release a memory region, it should call the
* {@link #closeScope(jdk.internal.misc.ScopedMemoryAccess.Scope)} method provided by this class. This method initiates
* thread-local handshakes with all the other VM threads, which are then stopped one by one. If any thread is found
* accessing memory that is associated to the very scope object being closed, that thread execution is asynchronously
* interrupted with a {@link Scope.ScopedAccessError}.
* <p>
* This synchronization strategy relies on the idea that accessing memory is atomic with respect to checking the
* validity of the scope associated with that memory region - that is, a thread that wants to perform memory access will be
* suspended either <em>before</em> a scope check or <em>after</em> the memory access. To ensure this atomicity,
* all methods in this class are marked with the special {@link Scoped} annotation, which is recognized by the VM,
* and used during the thread-local handshake to detect (and stop) threads performing potentially problematic memory access
* operations. Additionally, to make sure that the scope object(s) of the memory being accessed is always
* reachable during an access operation, all the methods in this class add reachability fences around the underlying
* unsafe access.
* <p>
* This form of synchronization allows APIs to use plain memory access without any other form of synchronization
* which might be deemed to expensive; in other words, this approach prioritizes the performance of memory access over
* that of releasing a shared memory resource.
*/
public class ScopedMemoryAccess {
private static Unsafe UNSAFE = Unsafe.getUnsafe();
private static native void registerNatives();
static {
registerNatives();
}
public boolean closeScope(Scope scope) {
return closeScope0(scope, Scope.ScopedAccessError.INSTANCE);
}
native boolean closeScope0(Scope scope, Scope.ScopedAccessError exception);
private ScopedMemoryAccess() {}
private static final ScopedMemoryAccess theScopedMemoryAccess = new ScopedMemoryAccess();
public static ScopedMemoryAccess getScopedMemoryAccess() {
return theScopedMemoryAccess;
}
/**
* Scope interface used during scoped memory access operations. A scope can be thought of as an object
* which embodies the temporal checks associated with a given memory region.
*/
public interface Scope {
void checkValidState();
Thread ownerThread();
/**
* Error thrown when memory access fails because the memory has already been released.
* Note: for performance reasons, this exception is never created by client; instead a shared instance
* is thrown (sometimes, this instance can be thrown asynchronously inside VM code). For this reason,
* it is important for clients to always catch this exception and throw a regular exception instead
* (which contains full stack information).
*/
final class ScopedAccessError extends Error {
private ScopedAccessError() {
super("Attempt to access an already released memory resource", null, false, false);
}
static final long serialVersionUID = 1L;
public static final ScopedAccessError INSTANCE = new ScopedAccessError();
}
}
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@interface Scoped { }
// bulk ops
@ForceInline
public void copyMemory(Scope srcScope, Scope dstScope,
Object srcBase, long srcOffset,
Object destBase, long destOffset,
long bytes) {
try {
copyMemoryInternal(srcScope, dstScope, srcBase, srcOffset, destBase, destOffset, bytes);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private void copyMemoryInternal(Scope srcScope, Scope dstScope,
Object srcBase, long srcOffset,
Object destBase, long destOffset,
long bytes) {
try {
if (srcScope != null) {
srcScope.checkValidState();
}
if (dstScope != null) {
dstScope.checkValidState();
}
UNSAFE.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes);
} finally {
Reference.reachabilityFence(srcScope);
Reference.reachabilityFence(dstScope);
}
}
@ForceInline
public void copySwapMemory(Scope srcScope, Scope dstScope,
Object srcBase, long srcOffset,
Object destBase, long destOffset,
long bytes, long elemSize) {
try {
copySwapMemoryInternal(srcScope, dstScope, srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private void copySwapMemoryInternal(Scope srcScope, Scope dstScope,
Object srcBase, long srcOffset,
Object destBase, long destOffset,
long bytes, long elemSize) {
try {
if (srcScope != null) {
srcScope.checkValidState();
}
if (dstScope != null) {
dstScope.checkValidState();
}
UNSAFE.copySwapMemory(srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
} finally {
Reference.reachabilityFence(srcScope);
Reference.reachabilityFence(dstScope);
}
}
@ForceInline
public void setMemory(Scope scope, Object o, long offset, long bytes, byte value) {
try {
setMemoryInternal(scope, o, offset, bytes, value);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private void setMemoryInternal(Scope scope, Object o, long offset, long bytes, byte value) {
try {
if (scope != null) {
scope.checkValidState();
}
UNSAFE.setMemory(o, offset, bytes, value);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public int vectorizedMismatch(Scope aScope, Scope bScope,
Object a, long aOffset,
Object b, long bOffset,
int length,
int log2ArrayIndexScale) {
try {
return vectorizedMismatchInternal(aScope, bScope, a, aOffset, b, bOffset, length, log2ArrayIndexScale);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
private int vectorizedMismatchInternal(Scope aScope, Scope bScope,
Object a, long aOffset,
Object b, long bOffset,
int length,
int log2ArrayIndexScale) {
try {
if (aScope != null) {
aScope.checkValidState();
}
if (bScope != null) {
bScope.checkValidState();
}
return ArraysSupport.vectorizedMismatch(a, aOffset, b, bOffset, length, log2ArrayIndexScale);
} finally {
Reference.reachabilityFence(aScope);
Reference.reachabilityFence(bScope);
}
}
@ForceInline
public boolean isLoaded(Scope scope, long address, boolean isSync, long size) {
try {
return isLoadedInternal(scope, address, isSync, size);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
public boolean isLoadedInternal(Scope scope, long address, boolean isSync, long size) {
try {
if (scope != null) {
scope.checkValidState();
}
return SharedSecrets.getJavaNioAccess().isLoaded(address, isSync, size);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public void load(Scope scope, long address, boolean isSync, long size) {
try {
loadInternal(scope, address, isSync, size);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
public void loadInternal(Scope scope, long address, boolean isSync, long size) {
try {
if (scope != null) {
scope.checkValidState();
}
SharedSecrets.getJavaNioAccess().load(address, isSync, size);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public void unload(Scope scope, long address, boolean isSync, long size) {
try {
unloadInternal(scope, address, isSync, size);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
public void unloadInternal(Scope scope, long address, boolean isSync, long size) {
try {
if (scope != null) {
scope.checkValidState();
}
SharedSecrets.getJavaNioAccess().unload(address, isSync, size);
} finally {
Reference.reachabilityFence(scope);
}
}
@ForceInline
public void force(Scope scope, FileDescriptor fd, long address, boolean isSync, long index, long length) {
try {
forceInternal(scope, fd, address, isSync, index, length);
} catch (Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@ForceInline @Scoped
public void forceInternal(Scope scope, FileDescriptor fd, long address, boolean isSync, long index, long length) {
try {
if (scope != null) {
scope.checkValidState();
}
SharedSecrets.getJavaNioAccess().force(fd, address, isSync, index, length);
} finally {
Reference.reachabilityFence(scope);
}
}
// typed-ops here

View File

@ -160,37 +160,6 @@ public class ArraysSupport {
}
}
/**
* Mismatch over long lengths.
*/
public static long vectorizedMismatchLargeForBytes(Object a, long aOffset,
Object b, long bOffset,
long length) {
long off = 0;
long remaining = length;
int i, size;
boolean lastSubRange = false;
while (remaining > 7 && !lastSubRange) {
if (remaining > Integer.MAX_VALUE) {
size = Integer.MAX_VALUE;
} else {
size = (int) remaining;
lastSubRange = true;
}
i = vectorizedMismatch(
a, aOffset + off,
b, bOffset + off,
size, LOG2_ARRAY_BYTE_INDEX_SCALE);
if (i >= 0)
return off + i;
i = size - ~i;
off += i;
remaining -= i;
}
return ~remaining;
}
// Booleans
// Each boolean element takes up one byte

View File

@ -217,7 +217,8 @@ module java.base {
exports jdk.internal.platform to
jdk.management;
exports jdk.internal.ref to
java.desktop;
java.desktop,
jdk.incubator.foreign;
exports jdk.internal.reflect to
java.logging,
java.sql,

View File

@ -0,0 +1,46 @@
/*
* 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 jdk.incubator.foreign;
/**
* Represents a type which is <em>addressable</em>. An addressable type is one which can be projected down to
* a memory address instance (see {@link #address()}). Examples of addressable types are {@link MemorySegment},
* and {@link MemoryAddress}.
*
* @apiNote In the future, if the Java language permits, {@link Addressable}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted types, such as {@link MemorySegment} and {@link MemoryAddress}.
*
* @implSpec
* Implementations of this interface <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*/
public interface Addressable {
/**
* Map this object into a {@link MemoryAddress} instance.
* @return the {@link MemoryAddress} instance associated with this object.
*/
MemoryAddress address();
}

View File

@ -1,132 +0,0 @@
/*
* 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 jdk.incubator.foreign;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
/**
* A mapped memory segment, that is, a memory segment backed by memory-mapped file.
*
* <p> Mapped memory segments are created via the {@link MemorySegment#mapFromPath(Path, long, long, FileChannel.MapMode)}.
* Mapped memory segments behave like ordinary segments, but provide additional capabilities to manipulate memory-mapped
* memory regions, such as {@link #force()} and {@link #load()}.
* <p>
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
* instances of {@code MemoryLayout} may have unpredictable results and should be avoided. The {@code equals} method should
* be used for comparisons.
* <p>
* Non-platform classes should not implement {@linkplain MappedMemorySegment} directly.
*
* <p> The content of a mapped memory segment can change at any time, for example
* if the content of the corresponding region of the mapped file is changed by
* this (or another) program. Whether or not such changes occur, and when they
* occur, is operating-system dependent and therefore unspecified.
*
* All or part of a mapped memory segment may become
* inaccessible at any time, for example if the backing mapped file is truncated. An
* attempt to access an inaccessible region of a mapped memory segment will not
* change the segment's content and will cause an unspecified exception to be
* thrown either at the time of the access or at some later time. It is
* therefore strongly recommended that appropriate precautions be taken to
* avoid the manipulation of a mapped file by this (or another) program, except to read or write
* the file's content.
*
* @apiNote In the future, if the Java language permits, {@link MemorySegment}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* explicitly permitted subtypes.
*/
public interface MappedMemorySegment extends MemorySegment {
@Override
MappedMemorySegment withAccessModes(int accessModes);
@Override
MappedMemorySegment asSlice(long offset, long newSize);
/**
* Forces any changes made to this segment's content to be written to the
* storage device containing the mapped file.
*
* <p> If the file mapped into this segment resides on a local storage
* device then when this method returns it is guaranteed that all changes
* made to the segment since it was created, or since this method was last
* invoked, will have been written to that device.
*
* <p> If the file does not reside on a local device then no such guarantee
* is made.
*
* <p> If this segment was not mapped in read/write mode ({@link
* java.nio.channels.FileChannel.MapMode#READ_WRITE}) then
* invoking this method may have no effect. In particular, the
* method has no effect for segments mapped in read-only or private
* mapping modes. This method may or may not have an effect for
* implementation-specific mapping modes.
* </p>
*/
void force();
/**
* Loads this segment's content into physical memory.
*
* <p> This method makes a best effort to ensure that, when it returns,
* this segment's contents is resident in physical memory. Invoking this
* method may cause some number of page faults and I/O operations to
* occur. </p>
*/
void load();
/**
* Unloads this segment's content from physical memory.
*
* <p> This method makes a best effort to ensure that this segment's contents are
* are no longer resident in physical memory. Accessing this segment's contents
* after invoking this method may cause some number of page faults and I/O operations to
* occur (as this segment's contents might need to be paged back in). </p>
*/
void unload();
/**
* Tells whether or not this segment's content is resident in physical
* memory.
*
* <p> A return value of {@code true} implies that it is highly likely
* that all of the data in this segment is resident in physical memory and
* may therefore be accessed without incurring any virtual-memory page
* faults or I/O operations. A return value of {@code false} does not
* necessarily imply that the segment's content is not resident in physical
* memory.
*
* <p> The returned value is a hint, rather than a guarantee, because the
* underlying operating system may have paged out some of the segment's data
* by the time that an invocation of this method returns. </p>
*
* @return {@code true} if it is likely that this segment's content
* is resident in physical memory
*/
boolean isLoaded();
}

View File

@ -0,0 +1,160 @@
/*
* 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 jdk.incubator.foreign;
import jdk.internal.foreign.MappedMemorySegmentImpl;
import java.nio.MappedByteBuffer;
/**
* This class provides capabilities to manipulate mapped memory segments, such as {@link #force(MemorySegment)},
* and {@link #load(MemorySegment)}. The methods in these class are suitable replacements for some of the
* functionality in the {@link java.nio.MappedByteBuffer} class. Note that, while it is possible to map a segment
* into a byte buffer (see {@link MemorySegment#asByteBuffer()}), and call e.g. {@link MappedByteBuffer#force()} that way,
* this can only be done when the source segment is small enough, due to the size limitation inherent to the
* ByteBuffer API.
* <p>
* Clients requiring sophisticated, low-level control over mapped memory segments, should consider writing
* custom mapped memory segment factories; using JNI, e.g. on Linux, it is possible to call {@code mmap}
* with the desired parameters; the returned address can be easily wrapped into a memory segment, using
* {@link MemoryAddress#ofLong(long)} and {@link MemoryAddress#asSegmentRestricted(long, Runnable, Object)}.
*
* @implNote
* The behavior of some the methods in this class (see {@link #load(MemorySegment)}, {@link #unload(MemorySegment)} and
* {@link #isLoaded(MemorySegment)}) is highly platform-dependent; as a result, calling these methods might
* be a no-op on certain platforms.
*/
public final class MappedMemorySegments {
private MappedMemorySegments() {
// no thanks
}
/**
* Tells whether or not the contents of the given segment is resident in physical
* memory.
*
* <p> A return value of {@code true} implies that it is highly likely
* that all of the data in the given segment is resident in physical memory and
* may therefore be accessed without incurring any virtual-memory page
* faults or I/O operations. A return value of {@code false} does not
* necessarily imply that the segment's content is not resident in physical
* memory.
*
* <p> The returned value is a hint, rather than a guarantee, because the
* underlying operating system may have paged out some of the segment's data
* by the time that an invocation of this method returns. </p>
*
* @param segment the segment whose contents are to be tested.
* @return {@code true} if it is likely that the contents of the given segment
* is resident in physical memory
*
* @throws IllegalStateException if the given segment is not alive, or if the given segment is confined
* and this method is called from a thread other than the segment's owner thread.
* @throws UnsupportedOperationException if the given segment is not a mapped memory segment, e.g. if
* {@code segment.isMapped() == false}.
*/
public static boolean isLoaded(MemorySegment segment) {
return toMappedSegment(segment).isLoaded();
}
/**
* Loads the contents of the given segment into physical memory.
*
* <p> This method makes a best effort to ensure that, when it returns,
* this contents of the given segment is resident in physical memory. Invoking this
* method may cause some number of page faults and I/O operations to
* occur. </p>
*
* @param segment the segment whose contents are to be loaded.
*
* @throws IllegalStateException if the given segment is not alive, or if the given segment is confined
* and this method is called from a thread other than the segment's owner thread.
* @throws UnsupportedOperationException if the given segment is not a mapped memory segment, e.g. if
* {@code segment.isMapped() == false}.
*/
public static void load(MemorySegment segment) {
toMappedSegment(segment).load();
}
/**
* Unloads the contents of the given segment from physical memory.
*
* <p> This method makes a best effort to ensure that the contents of the given segment are
* are no longer resident in physical memory. Accessing this segment's contents
* after invoking this method may cause some number of page faults and I/O operations to
* occur (as this segment's contents might need to be paged back in). </p>
*
* @param segment the segment whose contents are to be unloaded.
*
* @throws IllegalStateException if the given segment is not alive, or if the given segment is confined
* and this method is called from a thread other than the segment's owner thread.
* @throws UnsupportedOperationException if the given segment is not a mapped memory segment, e.g. if
* {@code segment.isMapped() == false}.
*/
public static void unload(MemorySegment segment) {
toMappedSegment(segment).unload();
}
/**
* Forces any changes made to the contents of the given segment to be written to the
* storage device described by the mapped segment's file descriptor.
*
* <p> If this mapping's file descriptor resides on a local storage
* device then when this method returns it is guaranteed that all changes
* made to the segment since it was created, or since this method was last
* invoked, will have been written to that device.
*
* <p> If this mapping's file descriptor does not reside on a local device then no such guarantee
* is made.
*
* <p> If the given segment was not mapped in read/write mode ({@link
* java.nio.channels.FileChannel.MapMode#READ_WRITE}) then
* invoking this method may have no effect. In particular, the
* method has no effect for segments mapped in read-only or private
* mapping modes. This method may or may not have an effect for
* implementation-specific mapping modes.
* </p>
*
* @param segment the segment whose contents are to be written to the storage device described by the
* segment's file descriptor.
*
* @throws IllegalStateException if the given segment is not alive, or if the given segment is confined
* and this method is called from a thread other than the segment's owner thread.
* @throws UnsupportedOperationException if the given segment is not a mapped memory segment, e.g. if
* {@code segment.isMapped() == false}.
*/
public static void force(MemorySegment segment) {
toMappedSegment(segment).force();
}
static MappedMemorySegmentImpl toMappedSegment(MemorySegment segment) {
if (segment instanceof MappedMemorySegmentImpl) {
return (MappedMemorySegmentImpl)segment;
} else {
throw new UnsupportedOperationException("Not a mapped memory segment");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -26,19 +26,18 @@
package jdk.incubator.foreign;
import jdk.internal.foreign.AbstractMemorySegmentImpl;
import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import jdk.internal.foreign.Utils;
import java.lang.ref.Cleaner;
/**
* A memory address models a reference into a memory location. Memory addresses are typically obtained using the
* {@link MemorySegment#baseAddress()} method; such addresses are said to be <em>checked</em>, and can be expressed
* as <em>offsets</em> into some underlying memory segment (see {@link #segment()} and {@link #segmentOffset()}).
* Since checked memory addresses feature both spatial and temporal bounds, these addresses can <em>safely</em> be
* dereferenced using a memory access var handle (see {@link MemoryHandles}).
* <p>
* If an address does not have any associated segment, it is said to be <em>unchecked</em>. Unchecked memory
* addresses do not feature known spatial or temporal bounds; as such, attempting a memory dereference operation
* using an unchecked memory address will result in a runtime exception. Unchecked addresses can be obtained
* e.g. by calling the {@link #ofLong(long)} method.
* {@link MemorySegment#address()} method, and can refer to either off-heap or on-heap memory.
* Given an address, it is possible to compute its offset relative to a given segment, which can be useful
* when performing memory dereference operations using a memory access var handle (see {@link MemoryHandles}).
* <p>
* All implementations of this interface must be <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>;
* use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on
@ -54,7 +53,13 @@ import jdk.internal.foreign.MemoryAddressImpl;
* @implSpec
* Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*/
public interface MemoryAddress {
public interface MemoryAddress extends Addressable {
@Override
default MemoryAddress address() {
return this;
}
/**
* Creates a new memory address with given offset (in bytes), which might be negative, from current one.
* @param offset specified offset (in bytes), relative to this address, which should be used to create the new address.
@ -63,35 +68,100 @@ public interface MemoryAddress {
MemoryAddress addOffset(long offset);
/**
* Returns the offset of this memory address into the underlying segment (if any).
* @return the offset of this memory address into the underlying segment (if any).
* @throws UnsupportedOperationException if no segment is associated with this memory address,
* e.g. if {@code segment() == null}.
* Returns the offset of this memory address into the given segment. More specifically, if both the segment's
* base address and this address are off-heap addresses, the result is computed as
* {@code this.toRawLongValue() - segment.address().toRawLongValue()}. Otherwise, if both addresses in the form
* {@code (B, O1)}, {@code (B, O2)}, where {@code B} is the same base heap object and {@code O1}, {@code O2}
* are byte offsets (relative to the base object) associated with this address and the segment's base address,
* the result is computed as {@code O1 - O2}.
* <p>
* If the segment's base address and this address are both heap addresses, but with different base objects, the result is undefined
* and an exception is thrown. Similarly, if the segment's base address is an heap address (resp. off-heap) and
* this address is an off-heap (resp. heap) address, the result is undefined and an exception is thrown.
* Otherwise, the result is a byte offset {@code SO}. If this address falls within the
* spatial bounds of the given segment, then {@code 0 <= SO < segment.byteSize()}; otherwise, {@code SO < 0 || SO > segment.byteSize()}.
* @return the offset of this memory address into the given segment.
* @param segment the segment relative to which this address offset should be computed
* @throws IllegalArgumentException if {@code segment} is not compatible with this address; this can happen, for instance,
* when {@code segment} models an heap memory region, while this address models an off-heap memory address.
*/
long segmentOffset();
long segmentOffset(MemorySegment segment);
/**
* Returns the raw long value associated to this memory address.
* @return The raw long value associated to this memory address.
* @throws UnsupportedOperationException if this memory address is associated with an heap segment.
* Returns a new confined native memory segment with given size, and whose base address is this address; the returned segment has its own temporal
* bounds, and can therefore be closed. This method can be useful when interacting with custom native memory sources (e.g. custom allocators),
* where an address to some underlying memory region is typically obtained from native code (often as a plain {@code long} value).
* <p>
* The returned segment will feature all <a href="#access-modes">access modes</a>
* (see {@link MemorySegment#ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}).
* <p>
* Clients should ensure that the address and bounds refers to a valid region of memory that is accessible for reading and,
* if appropriate, writing; an attempt to access an invalid memory location from Java code will either return an arbitrary value,
* have no visible effect, or cause an unspecified exception to be thrown.
* <p>
* Calling {@link MemorySegment#close()} on the returned segment will <em>not</em> result in releasing any
* memory resources which might implicitly be associated with the segment. This method is equivalent to the following code:
* <pre>{@code
asSegmentRestricted(byteSize, null, null);
* }</pre>
* This method is <em>restricted</em>. Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param bytesSize the desired size.
* @return a new confined native memory segment with given base address and size.
* @throws IllegalArgumentException if {@code bytesSize <= 0}.
* @throws UnsupportedOperationException if this address is an heap address.
* @throws IllegalAccessError if the runtime property {@code foreign.restricted} is not set to either
* {@code permit}, {@code warn} or {@code debug} (the default value is set to {@code deny}).
*/
default MemorySegment asSegmentRestricted(long bytesSize) {
return asSegmentRestricted(bytesSize, null, null);
}
/**
* Returns a new confined native memory segment with given size, and whose base address is this address; the returned segment has its own temporal
* bounds, and can therefore be closed. This method can be useful when interacting with custom native memory sources (e.g. custom allocators),
* where an address to some underlying memory region is typically obtained from native code (often as a plain {@code long} value).
* <p>
* The returned segment will feature all <a href="#access-modes">access modes</a>
* (see {@link MemorySegment#ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}).
* Moreover, the returned segment will keep a strong reference to the supplied attachment object (if any), which can
* be useful in cases where the lifecycle of the segment is dependent on that of some other external resource.
* <p>
* Clients should ensure that the address and bounds refers to a valid region of memory that is accessible for reading and,
* if appropriate, writing; an attempt to access an invalid memory location from Java code will either return an arbitrary value,
* have no visible effect, or cause an unspecified exception to be thrown.
* <p>
* Calling {@link MemorySegment#close()} on the returned segment will <em>not</em> result in releasing any
* memory resources which might implicitly be associated with the segment, but will result in calling the
* provided cleanup action (if any).
* <p>
* Both the cleanup action and the attachment object (if any) will be preserved under terminal operations such as
* {@link MemorySegment#handoff(Thread)}, {@link MemorySegment#share()} and {@link MemorySegment#registerCleaner(Cleaner)}.
* <p>
* This method is <em>restricted</em>. Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param bytesSize the desired size.
* @param cleanupAction the cleanup action; can be {@code null}.
* @param attachment an attachment object that will be kept strongly reachable by the returned segment; can be {@code null}.
* @return a new confined native memory segment with given base address and size.
* @throws IllegalArgumentException if {@code bytesSize <= 0}.
* @throws UnsupportedOperationException if this address is an heap address.
* @throws IllegalAccessError if the runtime property {@code foreign.restricted} is not set to either
* {@code permit}, {@code warn} or {@code debug} (the default value is set to {@code deny}).
*/
MemorySegment asSegmentRestricted(long bytesSize, Runnable cleanupAction, Object attachment);
/**
* Returns the raw long value associated with this memory address.
* @return The raw long value associated with this memory address.
* @throws UnsupportedOperationException if this memory address is an heap address.
*/
long toRawLongValue();
/**
* Returns the memory segment (if any) this address belongs to.
* @return The memory segment this address belongs to, or {@code null} if no such segment exists.
*/
MemorySegment segment();
/**
* Reinterpret this address as an offset into the provided segment.
* @param segment the segment to be rebased
* @return a new address pointing to the same memory location through the provided segment
* @throws IllegalArgumentException if the provided segment is not a valid rebase target for this address. This
* can happen, for instance, if an heap-based addressed is rebased to an off-heap memory segment.
*/
MemoryAddress rebase(MemorySegment segment);
/**
* Compares the specified object with this address for equality. Returns {@code true} if and only if the specified
* object is also an address, and it refers to the same memory location as this address.
@ -116,21 +186,18 @@ public interface MemoryAddress {
int hashCode();
/**
* The <em>unchecked</em> memory address instance modelling the {@code NULL} address. This address is <em>not</em> backed by
* a memory segment and hence it cannot be dereferenced.
* The off-heap memory address instance modelling the {@code NULL} address.
*/
MemoryAddress NULL = new MemoryAddressImpl( 0L);
MemoryAddress NULL = new MemoryAddressImpl(null, 0L);
/**
* Obtain a new <em>unchecked</em> memory address instance from given long address. The returned address is <em>not</em> backed by
* a memory segment and hence it cannot be dereferenced.
* Obtain an off-heap memory address instance from given long address.
* @param value the long address.
* @return the new memory address instance.
*/
static MemoryAddress ofLong(long value) {
return value == 0 ?
NULL :
new MemoryAddressImpl(value);
new MemoryAddressImpl(null, value);
}
}

View File

@ -46,44 +46,22 @@ import java.util.Objects;
* (all primitive types but {@code void} and {@code boolean} are supported), as well as the alignment constraint and the
* byte order associated to a memory access var handle. The resulting memory access var handle can then be combined in various ways
* to emulate different addressing modes. The var handles created by this class feature a <em>mandatory</em> coordinate type
* (of type {@link MemoryAddress}), and zero or more {@code long} coordinate types, which can be used to emulate
* multi-dimensional array indexing.
* (of type {@link MemorySegment}), and one {@code long} coordinate type, which represents the offset, in bytes, relative
* to the segment, at which dereference should occur.
* <p>
* As an example, consider the memory layout expressed by a {@link SequenceLayout} instance constructed as follows:
* As an example, consider the memory layout expressed by a {@link GroupLayout} instance constructed as follows:
* <blockquote><pre>{@code
SequenceLayout seq = MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
GroupLayout seq = MemoryLayout.ofStruct(
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
));
);
* }</pre></blockquote>
* To access the member layout named {@code value}, we can construct a memory access var handle as follows:
* <blockquote><pre>{@code
VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); //(MemoryAddress) -> int
handle = MemoryHandles.withOffset(handle, 4); //(MemoryAddress) -> int
handle = MemoryHandles.withStride(handle, 8); //(MemoryAddress, long) -> int
VarHandle handle = MemoryHandles.varHandle(int.class, ByteOrder.BIG_ENDIAN); //(MemorySegment, long) -> int
handle = MemoryHandles.insertCoordinates(handle, 1, 4); //(MemorySegment) -> int
* }</pre></blockquote>
*
* <h2>Addressing mode</h2>
*
* The final memory location accessed by a memory access var handle can be computed as follows:
*
* <blockquote><pre>{@code
address = base + offset
* }</pre></blockquote>
*
* where {@code base} denotes the address expressed by the {@link MemoryAddress} access coordinate, and {@code offset}
* can be expressed in the following form:
*
* <blockquote><pre>{@code
offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
* }</pre></blockquote>
*
* where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as optional {@code long}
* access coordinates, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
* <em>static</em> constants which are can be acquired through the {@link MemoryHandles#withOffset(VarHandle, long)}
* and the {@link MemoryHandles#withStride(VarHandle, long)} combinators, respectively.
*
* <h2><a id="memaccess-mode"></a>Alignment and access modes</h2>
*
* A memory access var handle is associated with an access size {@code S} and an alignment constraint {@code B}
@ -130,9 +108,6 @@ public final class MemoryHandles {
private static final MethodHandle LONG_TO_ADDRESS;
private static final MethodHandle ADDRESS_TO_LONG;
private static final MethodHandle ADD_OFFSET;
private static final MethodHandle ADD_STRIDE;
private static final MethodHandle INT_TO_BYTE;
private static final MethodHandle BYTE_TO_UNSIGNED_INT;
private static final MethodHandle INT_TO_SHORT;
@ -150,12 +125,6 @@ public final class MemoryHandles {
MethodType.methodType(MemoryAddress.class, long.class));
ADDRESS_TO_LONG = MethodHandles.lookup().findStatic(MemoryHandles.class, "addressToLong",
MethodType.methodType(long.class, MemoryAddress.class));
ADD_OFFSET = MethodHandles.lookup().findStatic(MemoryHandles.class, "addOffset",
MethodType.methodType(MemoryAddress.class, MemoryAddress.class, long.class));
ADD_STRIDE = MethodHandles.lookup().findStatic(MemoryHandles.class, "addStride",
MethodType.methodType(MemoryAddress.class, MemoryAddress.class, long.class, long.class));
INT_TO_BYTE = MethodHandles.explicitCastArguments(MethodHandles.identity(byte.class),
MethodType.methodType(byte.class, int.class));
BYTE_TO_UNSIGNED_INT = MethodHandles.lookup().findStatic(Byte.class, "toUnsignedInt",
@ -184,11 +153,12 @@ public final class MemoryHandles {
/**
* Creates a memory access var handle with the given carrier type and byte order.
*
* The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
* and its variable type is set by the given carrier type.
*
* The alignment constraint for the resulting memory access var handle is the same as the in memory size of the
* carrier type, and the accessed offset is set at zero.
* The returned var handle's type is {@code carrier} and the list of coordinate types is
* {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into
* a given memory segment. The returned var handle accesses bytes at an offset in a given
* memory segment, composing bytes to or from a value of the type {@code carrier} according to the given endianness;
* the alignment constraint (in bytes) for the resulting memory access var handle is the same as the size (in bytes) of the
* carrier type {@code carrier}.
*
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
* which are common to all memory access var handles.
@ -209,10 +179,11 @@ public final class MemoryHandles {
/**
* Creates a memory access var handle with the given carrier type, alignment constraint, and byte order.
*
* The resulting memory access var handle features a single {@link MemoryAddress} access coordinate,
* and its variable type is set by the given carrier type.
*
* The accessed offset is zero.
* The returned var handle's type is {@code carrier} and the list of coordinate types is
* {@code (MemorySegment, long)}, where the {@code long} coordinate type corresponds to byte offset into
* a given memory segment. The returned var handle accesses bytes at an offset in a given
* memory segment, composing bytes to or from a value of the type {@code carrier} according to the given endianness;
* the alignment constraint (in bytes) for the resulting memory access var handle is given by {@code alignmentBytes}.
*
* @apiNote the resulting var handle features certain <a href="#memaccess-mode">access mode restrictions</a>,
* which are common to all memory access var handles.
@ -232,94 +203,11 @@ public final class MemoryHandles {
throw new IllegalArgumentException("Bad alignment: " + alignmentBytes);
}
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, alignmentBytes - 1, byteOrder, 0, new long[]{}));
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, false, alignmentBytes - 1, byteOrder));
}
/**
* Returns a var handle that adds a <em>fixed</em> offset to the incoming {@link MemoryAddress} coordinate
* and then propagates such value to the target var handle. That is,
* when the returned var handle receives a memory address coordinate pointing at a memory location at
* offset <em>O</em>, a memory address coordinate pointing at a memory location at offset <em>O' + O</em>
* is created, and then passed to the target var handle.
*
* The returned var handle will feature the same type and access coordinates as the target var handle.
*
* @param target the target memory access handle to access after the offset adjustment.
* @param bytesOffset the offset, in bytes. Must be positive or zero.
* @return the adapted var handle.
* @throws IllegalArgumentException if the first access coordinate type is not of type {@link MemoryAddress}.
*/
public static VarHandle withOffset(VarHandle target, long bytesOffset) {
if (bytesOffset == 0) {
return target; //nothing to do
}
checkAddressFirstCoordinate(target);
if (JLI.isMemoryAccessVarHandle(target) &&
(bytesOffset & JLI.memoryAddressAlignmentMask(target)) == 0) {
//flatten
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(
JLI.memoryAddressCarrier(target),
JLI.memoryAddressAlignmentMask(target),
JLI.memoryAddressByteOrder(target),
JLI.memoryAddressOffset(target) + bytesOffset,
JLI.memoryAddressStrides(target)));
} else {
//slow path
VarHandle res = collectCoordinates(target, 0, ADD_OFFSET);
return insertCoordinates(res, 1, bytesOffset);
}
}
/**
* Returns a var handle which adds a <em>variable</em> offset to the incoming {@link MemoryAddress}
* access coordinate value and then propagates such value to the target var handle.
* That is, when the returned var handle receives a memory address coordinate pointing at a memory location at
* offset <em>O</em>, a new memory address coordinate pointing at a memory location at offset <em>(S * X) + O</em>
* is created, and then passed to the target var handle,
* where <em>S</em> is a constant <em>stride</em>, whereas <em>X</em> is a dynamic value that will be
* provided as an additional access coordinate (of type {@code long}).
*
* The returned var handle will feature the same type as the target var handle; an additional access coordinate
* of type {@code long} will be added to the access coordinate types of the target var handle at the position
* immediately following the leading access coordinate of type {@link MemoryAddress}.
*
* @param target the target memory access handle to access after the scale adjustment.
* @param bytesStride the stride, in bytes, by which to multiply the coordinate value.
* @return the adapted var handle.
* @throws IllegalArgumentException if the first access coordinate type is not of type {@link MemoryAddress}.
*/
public static VarHandle withStride(VarHandle target, long bytesStride) {
if (bytesStride == 0) {
return dropCoordinates(target, 1, long.class); // dummy coordinate
}
checkAddressFirstCoordinate(target);
if (JLI.isMemoryAccessVarHandle(target) &&
(bytesStride & JLI.memoryAddressAlignmentMask(target)) == 0) {
//flatten
long[] strides = JLI.memoryAddressStrides(target);
long[] newStrides = new long[strides.length + 1];
System.arraycopy(strides, 0, newStrides, 1, strides.length);
newStrides[0] = bytesStride;
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(
JLI.memoryAddressCarrier(target),
JLI.memoryAddressAlignmentMask(target),
JLI.memoryAddressByteOrder(target),
JLI.memoryAddressOffset(target),
newStrides));
} else {
//slow path
VarHandle res = collectCoordinates(target, 0, ADD_STRIDE);
return insertCoordinates(res, 2, bytesStride);
}
}
/**
* Adapt an existing var handle into a new var handle whose carrier type is {@link MemoryAddress}.
* Adapt an existing var handle into a new var handle whose carrier type is {@link MemorySegment}.
* That is, when calling {@link VarHandle#get(Object...)} on the returned var handle,
* the read numeric value will be turned into a memory address (as if by calling {@link MemoryAddress#ofLong(long)});
* similarly, when calling {@link VarHandle#set(Object...)}, the memory address to be set will be converted
@ -362,8 +250,8 @@ public final class MemoryHandles {
MemorySegment segment = MemorySegment.allocateNative(2);
VarHandle SHORT_VH = MemoryLayouts.JAVA_SHORT.varHandle(short.class);
VarHandle INT_VH = MemoryHandles.asUnsigned(SHORT_VH, int.class);
SHORT_VH.set(segment.baseAddress(), (short)-1);
INT_VH.get(segment.baseAddress()); // returns 65535
SHORT_VH.set(segment, (short)-1);
INT_VH.get(segment); // returns 65535
* }</pre></blockquote>
* <p>
* When calling e.g. {@link VarHandle#set(Object...)} on the resulting var
@ -620,8 +508,8 @@ public final class MemoryHandles {
private static void checkAddressFirstCoordinate(VarHandle handle) {
if (handle.coordinateTypes().size() < 1 ||
handle.coordinateTypes().get(0) != MemoryAddress.class) {
throw new IllegalArgumentException("Expected var handle with leading coordinate of type MemoryAddress");
handle.coordinateTypes().get(0) != MemorySegment.class) {
throw new IllegalArgumentException("Expected var handle with leading coordinate of type MemorySegment");
}
}
@ -662,12 +550,4 @@ public final class MemoryHandles {
private static long addressToLong(MemoryAddress value) {
return value.toRawLongValue();
}
private static MemoryAddress addOffset(MemoryAddress address, long offset) {
return address.addOffset(offset);
}
private static MemoryAddress addStride(MemoryAddress address, long index, long stride) {
return address.addOffset(index * stride);
}
}

View File

@ -68,9 +68,9 @@ import java.util.stream.Stream;
* <blockquote><pre>{@code
SequenceLayout taggedValues = MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
MemoryLayout.ofValueBits(8, ByteOrder.NATIVE_ORDER).withName("kind"),
MemoryLayout.ofValueBits(8, ByteOrder.nativeOrder()).withName("kind"),
MemoryLayout.ofPaddingBits(24),
MemoryLayout.ofValueBits(32, ByteOrder.NATIVE_ORDER).withName("value")
MemoryLayout.ofValueBits(32, ByteOrder.nativeOrder()).withName("value")
)
).withName("TaggedValues");
* }</pre></blockquote>
@ -147,7 +147,7 @@ MemoryLayout taggedValuesWithHole = taggedValues.map(l -> MemoryLayout.ofPadding
* <blockquote><pre>{@code
MemoryLayout taggedValuesWithHole = MemoryLayout.ofSequence(5,
MemoryLayout.ofStruct(
MemoryLayout.ofValueBits(8, ByteOrder.NATIVE_ORDER).withName("kind").
MemoryLayout.ofValueBits(8, ByteOrder.nativeOrder()).withName("kind").
MemoryLayout.ofPaddingBits(32),
MemoryLayout.ofPaddingBits(32)
));
@ -370,6 +370,24 @@ public interface MemoryLayout extends Constable {
/**
* Creates a memory access var handle that can be used to dereference memory at the layout selected by a given layout path,
* where the path is considered rooted in this layout.
* <p>
* The final memory location accessed by the returned memory access var handle can be computed as follows:
*
* <blockquote><pre>{@code
address = base + offset
* }</pre></blockquote>
*
* where {@code base} denotes the base address expressed by the {@link MemorySegment} access coordinate
* (see {@link MemorySegment#address()} and {@link MemoryAddress#toRawLongValue()}) and {@code offset}
* can be expressed in the following form:
*
* <blockquote><pre>{@code
offset = c_1 + c_2 + ... + c_m + (x_1 * s_1) + (x_2 * s_2) + ... + (x_n * s_n)
* }</pre></blockquote>
*
* where {@code x_1}, {@code x_2}, ... {@code x_n} are <em>dynamic</em> values provided as optional {@code long}
* access coordinates, whereas {@code c_1}, {@code c_2}, ... {@code c_m} and {@code s_0}, {@code s_1}, ... {@code s_n} are
* <em>static</em> stride constants which are derived from the layout path.
*
* @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every
* unspecified sequence access component contained in this layout path. Moreover, the resulting var handle

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 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
@ -26,6 +26,8 @@
package jdk.incubator.foreign;
import jdk.internal.misc.Unsafe;
import java.nio.ByteOrder;
/**
@ -101,6 +103,11 @@ public final class MemoryLayouts {
*/
public static final MemoryLayout PAD_64 = MemoryLayout.ofPaddingBits(64);
/**
* A value layout constant whose size is the same as that of a machine address (e.g. {@code size_t}), and byte order set to {@link ByteOrder#nativeOrder()}.
*/
public static final ValueLayout ADDRESS = MemoryLayout.ofValueBits(Unsafe.ADDRESS_SIZE * 8, ByteOrder.nativeOrder());
/**
* A value layout constant whose size is the same as that of a Java {@code byte}, and byte order set to {@link ByteOrder#nativeOrder()}.
*/
@ -123,8 +130,14 @@ public final class MemoryLayouts {
/**
* A value layout constant whose size is the same as that of a Java {@code long}, and byte order set to {@link ByteOrder#nativeOrder()}.
* The alignment of this layout (see {@link MemoryLayout#byteAlignment()} is platform-dependent, so that the following
* invariant holds:
* <blockquote><pre>{@code
MemoryLayouts.JAVA_LONG.byteAlignment() == MemoryLayouts.ADDRESS.byteSize();
* }</pre></blockquote>
*/
public static final ValueLayout JAVA_LONG = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder());
public static final ValueLayout JAVA_LONG = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder())
.withBitAlignment(ADDRESS.bitSize());
/**
* A value layout constant whose size is the same as that of a Java {@code float}, and byte order set to {@link ByteOrder#nativeOrder()}.
@ -133,6 +146,12 @@ public final class MemoryLayouts {
/**
* A value layout constant whose size is the same as that of a Java {@code double}, and byte order set to {@link ByteOrder#nativeOrder()}.
* The alignment of this layout (see {@link MemoryLayout#byteAlignment()} is platform-dependent, so that the following
* invariant holds:
* <blockquote><pre>{@code
MemoryLayouts.JAVA_DOUBLE.byteAlignment() == MemoryLayouts.ADDRESS.byteSize();
* }</pre></blockquote>
*/
public static final ValueLayout JAVA_DOUBLE = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder());
public static final ValueLayout JAVA_DOUBLE = MemoryLayout.ofValueBits(64, ByteOrder.nativeOrder())
.withBitAlignment(ADDRESS.bitSize());
}

View File

@ -26,6 +26,8 @@
package jdk.incubator.foreign;
import java.io.FileDescriptor;
import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import jdk.internal.foreign.AbstractMemorySegmentImpl;
@ -38,8 +40,8 @@ import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.Spliterator;
import java.util.function.Consumer;
/**
* A memory segment models a contiguous region of memory. A memory segment is associated with both spatial
@ -54,7 +56,7 @@ import java.util.function.Consumer;
* <p>
* Non-platform classes should not implement {@linkplain MemorySegment} directly.
*
* <h2>Constructing memory segments from different sources</h2>
* <h2>Constructing memory segments</h2>
*
* There are multiple ways to obtain a memory segment. First, memory segments backed by off-heap memory can
* be allocated using one of the many factory methods provided (see {@link MemorySegment#allocateNative(MemoryLayout)},
@ -74,8 +76,9 @@ import java.util.function.Consumer;
* by native memory.
* <p>
* Finally, it is also possible to obtain a memory segment backed by a memory-mapped file using the factory method
* {@link MemorySegment#mapFromPath(Path, long, long, FileChannel.MapMode)}. Such memory segments are called <em>mapped memory segments</em>
* (see {@link MappedMemorySegment}).
* {@link MemorySegment#mapFile(Path, long, long, FileChannel.MapMode)}. Such memory segments are called <em>mapped memory segments</em>;
* mapped memory segments are associated with an underlying file descriptor. For more operations on mapped memory segments, please refer to the
* {@link MappedMemorySegments} class.
* <p>
* Array and buffer segments are effectively <em>views</em> over existing memory regions which might outlive the
* lifecycle of the segments derived from them, and can even be manipulated directly (e.g. via array access, or direct use
@ -84,7 +87,7 @@ import java.util.function.Consumer;
* associated with such segments remain inaccessible, and that said memory sources are never aliased by more than one segment
* at a time - e.g. so as to prevent concurrent modifications of the contents of an array, or buffer segment.
*
* <h2>Closing a memory segment</h2>
* <h2>Explicit deallocation</h2>
*
* Memory segments are closed explicitly (see {@link MemorySegment#close()}). When a segment is closed, it is no longer
* <em>alive</em> (see {@link #isAlive()}, and subsequent operation on the segment (or on any {@link MemoryAddress} instance
@ -105,7 +108,7 @@ import java.util.function.Consumer;
* <h2><a id = "access-modes">Access modes</a></h2>
*
* Memory segments supports zero or more <em>access modes</em>. Supported access modes are {@link #READ},
* {@link #WRITE}, {@link #CLOSE}, {@link #ACQUIRE} and {@link #HANDOFF}. The set of access modes supported by a segment alters the
* {@link #WRITE}, {@link #CLOSE}, {@link #SHARE} and {@link #HANDOFF}. The set of access modes supported by a segment alters the
* set of operations that are supported by that segment. For instance, attempting to call {@link #close()} on
* a segment which does not support the {@link #CLOSE} access mode will result in an exception.
* <p>
@ -144,68 +147,112 @@ MemorySegment roSegment = segment.withAccessModes(segment.accessModes() & ~WRITE
* the segment using a memory access var handle. Any attempt to perform such operations from a thread other than the
* owner thread will result in a runtime failure.
* <p>
* Memory segments support <em>serial thread confinement</em>; that is, ownership of a memory segment can change (see
* {@link #withOwnerThread(Thread)}). This allows, for instance, for two threads {@code A} and {@code B} to share
* a segment in a controlled, cooperative and race-free fashion.
* The {@link #handoff(Thread)} method can be used to change the thread-confinement properties of a memory segment.
* This method is, like {@link #close()}, a <em>terminal operation</em> which marks the original segment as not alive
* (see {@link #isAlive()}) and creates a <em>new</em> segment with the desired thread-confinement properties. Calling
* {@link #handoff(Thread)} is only possible if the segment features the corresponding {@link #HANDOFF} access mode.
* <p>
* In some cases, it might be useful for multiple threads to process the contents of the same memory segment concurrently
* (e.g. in the case of parallel processing); while memory segments provide strong confinement guarantees, it is possible
* to obtain a {@link Spliterator} from a segment, which can be used to slice the segment and allow multiple thread to
* work in parallel on disjoint segment slices (this assumes that the access mode {@link #ACQUIRE} is set).
* For instance, the following code can be used to sum all int values in a memory segment in parallel:
* For instance, if a client wants to transfer ownership of a segment to another (known) thread, it can do so as follows:
*
* <blockquote><pre>{@code
MemorySegment segment = ...
SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT);
VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
int sum = StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true)
.mapToInt(s -> (int)VH_int.get(s.baseAddress()))
.sum();
MemorySegment aSegment = segment.handoff(threadA);
* }</pre></blockquote>
*
* By doing so, the original segment is marked as not alive, and a new segment is returned whose owner thread
* is {@code threadA}; this allows, for instance, for two threads {@code A} and {@code B} to share
* a segment in a controlled, cooperative and race-free fashion (also known as <em>serial thread confinement</em>).
* <p>
* Alternatively, the {@link #share()} method can be used to remove thread ownership altogether; this is only possible
* if the segment features the corresponding {@link #SHARE} access mode. The following code shows how clients can
* obtain a shared segment:
*
* <blockquote><pre>{@code
MemorySegment segment = ...
MemorySegment sharedSegment = segment.share();
* }</pre></blockquote>
*
* Again here, the original segment is marked as not alive, and a new <em>shared</em> segment is returned which features no owner
* thread (e.g. {@link #ownerThread()} returns {@code null}). This might be useful when multiple threads need to process
* the contents of the same memory segment concurrently (e.g. in the case of parallel processing). For instance, a client
* might obtain a {@link Spliterator} from a shared segment, which can then be used to slice the segment and allow multiple
* threads to work in parallel on disjoint segment slices. The following code can be used to sum all int values in a memory segment in parallel:
*
* <blockquote><pre>{@code
SequenceLayout SEQUENCE_LAYOUT = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT);
try (MemorySegment segment = MemorySegment.allocateNative(SEQUENCE_LAYOUT).share()) {
VarHandle VH_int = SEQUENCE_LAYOUT.elementLayout().varHandle(int.class);
int sum = StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), true)
.mapToInt(s -> (int)VH_int.get(s.address()))
.sum();
}
* }</pre></blockquote>
*
* Once shared, a segment can be claimed back by a given thread (again using {@link #handoff(Thread)}); in fact, many threads
* can attempt to gain ownership of the same segment, concurrently, and only one of them is guaranteed to succeed.
* <p>
* When using shared segments, clients should make sure that no other thread is accessing the segment while
* the segment is being closed. If one or more threads attempts to access a segment concurrently while the
* segment is being closed, an exception might occur on both the accessing and the closing threads. Clients should
* refrain from attempting to close a segment repeatedly (e.g. keep calling {@link #close()} until no exception is thrown);
* such exceptions should instead be seen as an indication that the client code is lacking appropriate synchronization between the threads
* accessing/closing the segment.
*
* <h2>Implicit deallocation</h2>
*
* Clients can register a memory segment against a {@link Cleaner}, to make sure that underlying resources associated with
* that segment will be released when the segment becomes <em>unreachable</em>, which can be useful to prevent native memory
* leaks. This can be achieved using the {@link #registerCleaner(Cleaner)} method, as follows:
*
* <blockquote><pre>{@code
MemorySegment segment = ...
MemorySegment gcSegment = segment.registerCleaner(cleaner);
* }</pre></blockquote>
*
* Here, the original segment is marked as not alive, and a new segment is returned (the owner thread of the returned
* segment set is set to that of the current thread, see {@link #ownerThread()}); the new segment
* will also be registered with the the {@link Cleaner} instance provided to the {@link #registerCleaner(Cleaner)} method;
* as such, if not closed explicitly (see {@link #close()}), the new segment will be automatically closed by the cleaner.
*
* @apiNote In the future, if the Java language permits, {@link MemorySegment}
* may become a {@code sealed} interface, which would prohibit subclassing except by
* {@link MappedMemorySegment} and other explicitly permitted subtypes.
* may become a {@code sealed} interface, which would prohibit subclassing except by other explicitly permitted subtypes.
*
* @implSpec
* Implementations of this interface are immutable, thread-safe and <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>.
*/
public interface MemorySegment extends AutoCloseable {
public interface MemorySegment extends Addressable, AutoCloseable {
/**
* The base memory address associated with this memory segment. The returned address is
* a <em>checked</em> memory address and can therefore be used in derefrence operations
* a <em>checked</em> memory address and can therefore be used in dereference operations
* (see {@link MemoryAddress}).
* @return The base memory address.
* @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment
*/
MemoryAddress baseAddress();
@Override
MemoryAddress address();
/**
* Returns a spliterator for the given memory segment. The returned spliterator reports {@link Spliterator#SIZED},
* Returns a spliterator for this memory segment. The returned spliterator reports {@link Spliterator#SIZED},
* {@link Spliterator#SUBSIZED}, {@link Spliterator#IMMUTABLE}, {@link Spliterator#NONNULL} and {@link Spliterator#ORDERED}
* characteristics.
* <p>
* The returned spliterator splits the segment according to the specified sequence layout; that is,
* The returned spliterator splits this segment according to the specified sequence layout; that is,
* if the supplied layout is a sequence layout whose element count is {@code N}, then calling {@link Spliterator#trySplit()}
* will result in a spliterator serving approximatively {@code N/2} elements (depending on whether N is even or not).
* As such, splitting is possible as long as {@code N >= 2}. The spliterator returns segments that feature the same
* <a href="#access-modes">access modes</a> as the given segment less the {@link #CLOSE} access mode.
* <p>
* The returned spliterator effectively allows to slice a segment into disjoint sub-segments, which can then
* be processed in parallel by multiple threads (if the access mode {@link #ACQUIRE} is set).
* While closing the segment (see {@link #close()}) during pending concurrent execution will generally
* fail with an exception, it is possible to close a segment when a spliterator has been obtained but no thread
* is actively working on it using {@link Spliterator#tryAdvance(Consumer)}; in such cases, any subsequent call
* to {@link Spliterator#tryAdvance(Consumer)} will fail with an exception.
* @param segment the segment to be used for splitting.
* The returned spliterator effectively allows to slice this segment into disjoint sub-segments, which can then
* be processed in parallel by multiple threads (if the segment is shared).
*
* @param layout the layout to be used for splitting.
* @param <S> the memory segment type
* @return the element spliterator for this segment
* @throws IllegalStateException if the segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment
*/
static <S extends MemorySegment> Spliterator<S> spliterator(S segment, SequenceLayout layout) {
return AbstractMemorySegmentImpl.spliterator(segment, layout);
}
Spliterator<MemorySegment> spliterator(SequenceLayout layout);
/**
* The thread owning this segment.
@ -213,27 +260,6 @@ public interface MemorySegment extends AutoCloseable {
*/
Thread ownerThread();
/**
* Obtains a new memory segment backed by the same underlying memory region as this segment,
* but with different owner thread. As a side-effect, this segment will be marked as <em>not alive</em>,
* and subsequent operations on this segment will result in runtime errors.
* <p>
* Write accesses to the segment's content <a href="../../../java/util/concurrent/package-summary.html#MemoryVisibility"><i>happens-before</i></a>
* hand-over from the current owner thread to the new owner thread, which in turn <i>happens before</i> read accesses to the segment's contents on
* the new owner thread.
*
* @param newOwner the new owner thread.
* @return a new memory segment backed by the same underlying memory region as this segment,
* owned by {@code newOwner}.
* @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment, or if the segment cannot be closed because it is being operated upon by a different
* thread (see {@link #spliterator(MemorySegment, SequenceLayout)}).
* @throws NullPointerException if {@code newOwner == null}
* @throws IllegalArgumentException if the segment is already a confined segment owner by {@code newOnwer}.
* @throws UnsupportedOperationException if this segment does not support the {@link #HANDOFF} access mode.
*/
MemorySegment withOwnerThread(Thread newOwner);
/**
* The size (in bytes) of this memory segment.
* @return The size (in bytes) of this memory segment.
@ -242,7 +268,7 @@ public interface MemorySegment extends AutoCloseable {
/**
* Obtains a segment view with specific <a href="#access-modes">access modes</a>. Supported access modes are {@link #READ}, {@link #WRITE},
* {@link #CLOSE}, {@link #ACQUIRE} and {@link #HANDOFF}. It is generally not possible to go from a segment with stricter access modes
* {@link #CLOSE}, {@link #SHARE} and {@link #HANDOFF}. It is generally not possible to go from a segment with stricter access modes
* to one with less strict access modes. For instance, attempting to add {@link #WRITE} access mode to a read-only segment
* will be met with an exception.
* @param accessModes an ORed mask of zero or more access modes.
@ -262,7 +288,7 @@ public interface MemorySegment extends AutoCloseable {
/**
* Returns the <a href="#access-modes">access modes</a> associated with this segment; the result is represented as ORed values from
* {@link #READ}, {@link #WRITE}, {@link #CLOSE}, {@link #ACQUIRE} and {@link #HANDOFF}.
* {@link #READ}, {@link #WRITE}, {@link #CLOSE}, {@link #SHARE} and {@link #HANDOFF}.
* @return the access modes associated with this segment.
*/
int accessModes();
@ -270,6 +296,11 @@ public interface MemorySegment extends AutoCloseable {
/**
* Obtains a new memory segment view whose base address is the same as the base address of this segment plus a given offset,
* and whose new size is specified by the given argument.
*
* @see #asSlice(long)
* @see #asSlice(MemoryAddress)
* @see #asSlice(MemoryAddress, long)
*
* @param offset The new segment base offset (relative to the current segment base address), specified in bytes.
* @param newSize The new segment size, specified in bytes.
* @return a new memory segment view with updated base/limit addresses.
@ -277,6 +308,81 @@ public interface MemorySegment extends AutoCloseable {
*/
MemorySegment asSlice(long offset, long newSize);
/**
* Obtains a new memory segment view whose base address is the given address, and whose new size is specified by the given argument.
* <p>
* Equivalent to the following code:
* <pre>{@code
asSlice(newBase.segmentOffset(this), newSize);
* }</pre>
*
* @see #asSlice(long)
* @see #asSlice(MemoryAddress)
* @see #asSlice(long, long)
*
* @param newBase The new segment base address.
* @param newSize The new segment size, specified in bytes.
* @return a new memory segment view with updated base/limit addresses.
* @throws NullPointerException if {@code newBase == null}.
* @throws IndexOutOfBoundsException if {@code offset < 0}, {@code offset > byteSize()}, {@code newSize < 0}, or {@code newSize > byteSize() - offset}
*/
default MemorySegment asSlice(MemoryAddress newBase, long newSize) {
Objects.requireNonNull(newBase);
return asSlice(newBase.segmentOffset(this), newSize);
}
/**
* Obtains a new memory segment view whose base address is the same as the base address of this segment plus a given offset,
* and whose new size is computed by subtracting the specified offset from this segment size.
* <p>
* Equivalent to the following code:
* <pre>{@code
asSlice(offset, byteSize() - offset);
* }</pre>
*
* @see #asSlice(MemoryAddress)
* @see #asSlice(MemoryAddress, long)
* @see #asSlice(long, long)
*
* @param offset The new segment base offset (relative to the current segment base address), specified in bytes.
* @return a new memory segment view with updated base/limit addresses.
* @throws IndexOutOfBoundsException if {@code offset < 0}, or {@code offset > byteSize()}.
*/
default MemorySegment asSlice(long offset) {
return asSlice(offset, byteSize() - offset);
}
/**
* Obtains a new memory segment view whose base address is the given address, and whose new size is computed by subtracting
* the address offset relative to this segment (see {@link MemoryAddress#segmentOffset(MemorySegment)}) from this segment size.
* <p>
* Equivalent to the following code:
* <pre>{@code
asSlice(newBase.segmentOffset(this));
* }</pre>
*
* @see #asSlice(long)
* @see #asSlice(MemoryAddress, long)
* @see #asSlice(long, long)
*
* @param newBase The new segment base offset (relative to the current segment base address), specified in bytes.
* @return a new memory segment view with updated base/limit addresses.
* @throws NullPointerException if {@code newBase == null}.
* @throws IndexOutOfBoundsException if {@code address.segmentOffset(this) < 0}, or {@code address.segmentOffset(this) > byteSize()}.
*/
default MemorySegment asSlice(MemoryAddress newBase) {
Objects.requireNonNull(newBase);
return asSlice(newBase.segmentOffset(this));
}
/**
* Is this a mapped segment? Returns true if this segment is a mapped memory segment,
* created using the {@link #mapFile(Path, long, long, FileChannel.MapMode)} factory, or a buffer segment
* derived from a {@link java.nio.MappedByteBuffer} using the {@link #ofByteBuffer(ByteBuffer)} factory.
* @return {@code true} if this segment is a mapped segment.
*/
boolean isMapped();
/**
* Is this segment alive?
* @return true, if the segment is alive.
@ -285,17 +391,93 @@ public interface MemorySegment extends AutoCloseable {
boolean isAlive();
/**
* Closes this memory segment. Once a memory segment has been closed, any attempt to use the memory segment,
* or to access any {@link MemoryAddress} instance associated with it will fail with {@link IllegalStateException}.
* Closes this memory segment. This is a <em>terminal operation</em>; as a side-effect, if this operation completes
* without exceptions, this segment will be marked as <em>not alive</em>, and subsequent operations on this segment
* will fail with {@link IllegalStateException}.
* <p>
* Depending on the kind of memory segment being closed, calling this method further triggers deallocation of all the resources
* associated with the memory segment.
*
* @apiNote This operation is not idempotent; that is, closing an already closed segment <em>always</em> results in an
* exception being thrown. This reflects a deliberate design choice: segment state transitions should be
* manifest in the client code; a failure in any of these transitions reveals a bug in the underlying application
* logic. This is especially useful when reasoning about the lifecycle of dependent segment views (see {@link #asSlice(MemoryAddress)},
* where closing one segment might side-effect multiple segments. In such cases it might in fact not be obvious, looking
* at the code, as to whether a given segment is alive or not.
*
* @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment, or if the segment cannot be closed because it is being operated upon by a different
* thread (see {@link #spliterator(MemorySegment, SequenceLayout)}).
* thread owning this segment, or if this segment is shared and the segment is concurrently accessed while this method is
* called.
* @throws UnsupportedOperationException if this segment does not support the {@link #CLOSE} access mode.
*/
void close();
/**
* Obtains a new confined memory segment backed by the same underlying memory region as this segment. The returned segment will
* be confined on the specified thread, and will feature the same spatial bounds and access modes (see {@link #accessModes()})
* as this segment.
* <p>
* This is a <em>terminal operation</em>; as a side-effect, if this operation completes
* without exceptions, this segment will be marked as <em>not alive</em>, and subsequent operations on this segment
* will fail with {@link IllegalStateException}.
* <p>
* In case where the owner thread of the returned segment differs from that of this segment, write accesses to this
* segment's content <a href="../../../java/util/concurrent/package-summary.html#MemoryVisibility"><i>happens-before</i></a>
* hand-over from the current owner thread to the new owner thread, which in turn <i>happens before</i> read accesses
* to the returned segment's contents on the new owner thread.
*
* @param thread the new owner thread
* @return a new confined memory segment whose owner thread is set to {@code thread}.
* @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment.
* @throws UnsupportedOperationException if this segment does not support the {@link #HANDOFF} access mode.
* @throws NullPointerException if {@code thread == null}
*/
MemorySegment handoff(Thread thread);
/**
* Obtains a new shared memory segment backed by the same underlying memory region as this segment. The returned segment will
* not be confined on any thread and can therefore be accessed concurrently from multiple threads; moreover, the
* returned segment will feature the same spatial bounds and access modes (see {@link #accessModes()})
* as this segment.
* <p>
* This is a <em>terminal operation</em>; as a side-effect, if this operation completes
* without exceptions, this segment will be marked as <em>not alive</em>, and subsequent operations on this segment
* will fail with {@link IllegalStateException}.
* <p>
* Write accesses to this segment's content <a href="../../../java/util/concurrent/package-summary.html#MemoryVisibility"><i>happens-before</i></a>
* hand-over from the current owner thread to the new owner thread, which in turn <i>happens before</i> read accesses
* to the returned segment's contents on a new thread.
*
* @return a new memory shared segment backed by the same underlying memory region as this segment.
* @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment.
*/
MemorySegment share();
/**
* Register this memory segment instance against a {@link Cleaner} object, by returning a new memory segment backed
* by the same underlying memory region as this segment. The returned segment will feature the same confinement,
* spatial bounds and access modes (see {@link #accessModes()}) as this segment. Moreover, the returned segment
* will be associated with the specified {@link Cleaner} object; this allows for the segment to be closed
* as soon as it becomes <em>unreachable</em>, which might be helpful in preventing native memory leaks.
* <p>
* This is a <em>terminal operation</em>; as a side-effect, if this operation completes
* without exceptions, this segment will be marked as <em>not alive</em>, and subsequent operations on this segment
* will fail with {@link IllegalStateException}.
* <p>
* The implicit deallocation behavior associated with the returned segment will be preserved under terminal
* operations such as {@link #handoff(Thread)} and {@link #share()}.
*
* @param cleaner the cleaner object, responsible for implicit deallocation of the returned segment.
* @return a new memory segment backed by the same underlying memory region as this segment, which features
* implicit deallocation.
* @throws IllegalStateException if this segment is not <em>alive</em>, or if access occurs from a thread other than the
* thread owning this segment, or if this segment is already associated with a cleaner.
* @throws UnsupportedOperationException if this segment does not support the {@link #CLOSE} access mode.
*/
MemorySegment registerCleaner(Cleaner cleaner);
/**
* Fills a value into this memory segment.
* <p>
@ -306,7 +488,7 @@ public interface MemorySegment extends AutoCloseable {
byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
.varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
for (long l = 0; l < segment.byteSize(); l++) {
byteHandle.set(segment.baseAddress(), l, value);
byteHandle.set(segment.address(), l, value);
}
* }</pre>
*
@ -334,7 +516,7 @@ for (long l = 0; l < segment.byteSize(); l++) {
* <p>
* The result of a bulk copy is unspecified if, in the uncommon case, the source segment and this segment
* do not overlap, but refer to overlapping regions of the same backing storage using different addresses.
* For example, this may occur if the same file is {@link MemorySegment#mapFromPath mapped} to two segments.
* For example, this may occur if the same file is {@link MemorySegment#mapFile mapped} to two segments.
*
* @param src the source segment.
* @throws IndexOutOfBoundsException if {@code src.byteSize() > this.byteSize()}.
@ -349,7 +531,7 @@ for (long l = 0; l < segment.byteSize(); l++) {
/**
* Finds and returns the offset, in bytes, of the first mismatch between
* this segment and a given other segment. The offset is relative to the
* {@link #baseAddress() base address} of each segment and will be in the
* {@link #address() base address} of each segment and will be in the
* range of 0 (inclusive) up to the {@link #byteSize() size} (in bytes) of
* the smaller memory segment (exclusive).
* <p>
@ -379,11 +561,21 @@ for (long l = 0; l < segment.byteSize(); l++) {
* (see {@link ByteBuffer#isReadOnly()}. Additionally, if this is a native memory segment, the resulting buffer is
* <em>direct</em> (see {@link ByteBuffer#isDirect()}).
* <p>
* The returned buffer's position (see {@link ByteBuffer#position()} is initially set to zero, while
* the returned buffer's capacity and limit (see {@link ByteBuffer#capacity()} and {@link ByteBuffer#limit()}, respectively)
* are set to this segment' size (see {@link MemorySegment#byteSize()}). For this reason, a byte buffer cannot be
* returned if this segment' size is greater than {@link Integer#MAX_VALUE}.
* <p>
* The life-cycle of the returned buffer will be tied to that of this segment. That means that if the this segment
* is closed (see {@link MemorySegment#close()}, accessing the returned
* buffer will throw an {@link IllegalStateException}.
* <p>
* The resulting buffer's byte order is {@link java.nio.ByteOrder#BIG_ENDIAN}; this can be changed using
* If this segment is <em>shared</em>, calling certain I/O operations on the resulting buffer might result in
* an unspecified exception being thrown. Examples of such problematic operations are {@link FileChannel#read(ByteBuffer)},
* {@link FileChannel#write(ByteBuffer)}, {@link java.nio.channels.SocketChannel#read(ByteBuffer)} and
* {@link java.nio.channels.SocketChannel#write(ByteBuffer)}.
* <p>
* Finally, the resulting buffer's byte order is {@link java.nio.ByteOrder#BIG_ENDIAN}; this can be changed using
* {@link ByteBuffer#order(java.nio.ByteOrder)}.
*
* @return a {@link ByteBuffer} view of this memory segment.
@ -404,123 +596,190 @@ for (long l = 0; l < segment.byteSize(); l++) {
byte[] toByteArray();
/**
* Creates a new buffer memory segment that models the memory associated with the given byte
* Copy the contents of this memory segment into a fresh short array.
* @return a fresh short array copy of this memory segment.
* @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this
* segment's contents cannot be copied into a {@link short[]} instance, e.g. because {@code byteSize() % 2 != 0},
* or {@code byteSize() / 2 > Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
short[] toShortArray();
/**
* Copy the contents of this memory segment into a fresh char array.
* @return a fresh char array copy of this memory segment.
* @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this
* segment's contents cannot be copied into a {@link char[]} instance, e.g. because {@code byteSize() % 2 != 0},
* or {@code byteSize() / 2 > Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
char[] toCharArray();
/**
* Copy the contents of this memory segment into a fresh int array.
* @return a fresh int array copy of this memory segment.
* @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this
* segment's contents cannot be copied into a {@link int[]} instance, e.g. because {@code byteSize() % 4 != 0},
* or {@code byteSize() / 4 > Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
int[] toIntArray();
/**
* Copy the contents of this memory segment into a fresh float array.
* @return a fresh float array copy of this memory segment.
* @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this
* segment's contents cannot be copied into a {@link float[]} instance, e.g. because {@code byteSize() % 4 != 0},
* or {@code byteSize() / 4 > Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
float[] toFloatArray();
/**
* Copy the contents of this memory segment into a fresh long array.
* @return a fresh long array copy of this memory segment.
* @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this
* segment's contents cannot be copied into a {@link long[]} instance, e.g. because {@code byteSize() % 8 != 0},
* or {@code byteSize() / 8 > Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
long[] toLongArray();
/**
* Copy the contents of this memory segment into a fresh double array.
* @return a fresh double array copy of this memory segment.
* @throws UnsupportedOperationException if this segment does not feature the {@link #READ} access mode, or if this
* segment's contents cannot be copied into a {@link double[]} instance, e.g. because {@code byteSize() % 8 != 0},
* or {@code byteSize() / 8 > Integer#MAX_VALUE}.
* @throws IllegalStateException if this segment has been closed, or if access occurs from a thread other than the
* thread owning this segment.
*/
double[] toDoubleArray();
/**
* Creates a new confined buffer memory segment that models the memory associated with the given byte
* buffer. The segment starts relative to the buffer's position (inclusive)
* and ends relative to the buffer's limit (exclusive).
* <p>
* The segment will feature all <a href="#access-modes">access modes</a> (see {@link #ALL_ACCESS}),
* unless the given buffer is {@linkplain ByteBuffer#isReadOnly() read-only} in which case the segment will
* not feature the {@link #WRITE} access mode.
* not feature the {@link #WRITE} access mode, and its confinement thread is the current thread (see {@link Thread#currentThread()}).
* <p>
* The resulting memory segment keeps a reference to the backing buffer, to ensure it remains <em>reachable</em>
* for the life-time of the segment.
*
* @param bb the byte buffer backing the buffer memory segment.
* @return a new buffer memory segment.
* @return a new confined buffer memory segment.
*/
static MemorySegment ofByteBuffer(ByteBuffer bb) {
return AbstractMemorySegmentImpl.ofBuffer(bb);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated byte array.
* Creates a new confined array memory segment that models the memory associated with a given heap-allocated byte array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
* (see {@link #ALL_ACCESS}).
* (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}).
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
* @return a new confined array memory segment.
*/
static MemorySegment ofArray(byte[] arr) {
return HeapMemorySegmentImpl.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated char array.
* Creates a new confined array memory segment that models the memory associated with a given heap-allocated char array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
* (see {@link #ALL_ACCESS}).
* (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}).
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
* @return a new confined array memory segment.
*/
static MemorySegment ofArray(char[] arr) {
return HeapMemorySegmentImpl.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated short array.
* Creates a new confined array memory segment that models the memory associated with a given heap-allocated short array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
* (see {@link #ALL_ACCESS}).
* (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}).
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
* @return a new confined array memory segment.
*/
static MemorySegment ofArray(short[] arr) {
return HeapMemorySegmentImpl.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated int array.
* Creates a new confined array memory segment that models the memory associated with a given heap-allocated int array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>.
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
* (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}).
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
* @return a new confined array memory segment.
*/
static MemorySegment ofArray(int[] arr) {
return HeapMemorySegmentImpl.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated float array.
* Creates a new confined array memory segment that models the memory associated with a given heap-allocated float array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
* (see {@link #ALL_ACCESS}).
* (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}).
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
* @return a new confined array memory segment.
*/
static MemorySegment ofArray(float[] arr) {
return HeapMemorySegmentImpl.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated long array.
* Creates a new confined array memory segment that models the memory associated with a given heap-allocated long array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
* (see {@link #ALL_ACCESS}).
* (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}).
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
* @return a new confined array memory segment.
*/
static MemorySegment ofArray(long[] arr) {
return HeapMemorySegmentImpl.makeArraySegment(arr);
}
/**
* Creates a new array memory segment that models the memory associated with a given heap-allocated double array.
* Creates a new confined array memory segment that models the memory associated with a given heap-allocated double array.
* <p>
* The resulting memory segment keeps a reference to the backing array, to ensure it remains <em>reachable</em>
* for the life-time of the segment. The segment will feature all <a href="#access-modes">access modes</a>
* (see {@link #ALL_ACCESS}).
*
* @param arr the primitive array backing the array memory segment.
* @return a new array memory segment.
* @return a new confined array memory segment.
*/
static MemorySegment ofArray(double[] arr) {
return HeapMemorySegmentImpl.makeArraySegment(arr);
}
/**
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given layout.
* Creates a new confined native memory segment that models a newly allocated block of off-heap memory with given layout.
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
@ -540,7 +799,7 @@ for (long l = 0; l < segment.byteSize(); l++) {
}
/**
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given size (in bytes).
* Creates a new confined native memory segment that models a newly allocated block of off-heap memory with given size (in bytes).
* <p>
* This is equivalent to the following code:
* <blockquote><pre>{@code
@ -552,7 +811,7 @@ allocateNative(bytesSize, 1);
* to make sure the backing off-heap memory block is deallocated accordingly. Failure to do so will result in off-heap memory leaks.
*
* @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
* @return a new native memory segment.
* @return a new confined native memory segment.
* @throws IllegalArgumentException if {@code bytesSize < 0}.
*/
static MemorySegment allocateNative(long bytesSize) {
@ -560,11 +819,25 @@ allocateNative(bytesSize, 1);
}
/**
* Creates a new mapped memory segment that models a memory-mapped region of a file from a given path.
* Creates a new confined mapped memory segment that models a memory-mapped region of a file from a given path.
* <p>
* The segment will feature all <a href="#access-modes">access modes</a> (see {@link #ALL_ACCESS}),
* unless the given mapping mode is {@linkplain FileChannel.MapMode#READ_ONLY READ_ONLY}, in which case
* the segment will not feature the {@link #WRITE} access mode.
* the segment will not feature the {@link #WRITE} access mode, and its confinement thread is the current thread (see {@link Thread#currentThread()}).
* <p>
* The content of a mapped memory segment can change at any time, for example
* if the content of the corresponding region of the mapped file is changed by
* this (or another) program. Whether or not such changes occur, and when they
* occur, is operating-system dependent and therefore unspecified.
* <p>
* All or part of a mapped memory segment may become
* inaccessible at any time, for example if the backing mapped file is truncated. An
* attempt to access an inaccessible region of a mapped memory segment will not
* change the segment's content and will cause an unspecified exception to be
* thrown either at the time of the access or at some later time. It is
* therefore strongly recommended that appropriate precautions be taken to
* avoid the manipulation of a mapped file by this (or another) program, except to read or write
* the file's content.
*
* @implNote When obtaining a mapped segment from a newly created file, the initialization state of the contents of the block
* of mapped memory associated with the returned mapped memory segment is unspecified and should not be relied upon.
@ -573,21 +846,26 @@ allocateNative(bytesSize, 1);
* @param bytesOffset the offset (expressed in bytes) within the file at which the mapped segment is to start.
* @param bytesSize the size (in bytes) of the mapped memory backing the memory segment.
* @param mapMode a file mapping mode, see {@link FileChannel#map(FileChannel.MapMode, long, long)}; the chosen mapping mode
* might affect the behavior of the returned memory mapped segment (see {@link MappedMemorySegment#force()}).
* @return a new mapped memory segment.
* might affect the behavior of the returned memory mapped segment (see {@link MappedMemorySegments#force(MemorySegment)}).
* @return a new confined mapped memory segment.
* @throws IllegalArgumentException if {@code bytesOffset < 0}.
* @throws IllegalArgumentException if {@code bytesSize < 0}.
* @throws UnsupportedOperationException if an unsupported map mode is specified.
* @throws UnsupportedOperationException if an unsupported map mode is specified, or if the {@code path} is associated
* with a provider that does not support creating file channels.
* @throws IOException if the specified path does not point to an existing file, or if some other I/O error occurs.
* @throws SecurityException If a security manager is installed and it denies an unspecified permission required by the implementation.
* In the case of the default provider, the {@link SecurityManager#checkRead(String)} method is invoked to check
* read access if the file is opened for reading. The {@link SecurityManager#checkWrite(String)} method is invoked to check
* write access if the file is opened for writing.
*/
static MappedMemorySegment mapFromPath(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
static MemorySegment mapFile(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
return MappedMemorySegmentImpl.makeMappedSegment(path, bytesOffset, bytesSize, mapMode);
}
/**
* Creates a new native memory segment that models a newly allocated block of off-heap memory with given size and
* Creates a new confined native memory segment that models a newly allocated block of off-heap memory with given size and
* alignment constraint (in bytes). The segment will feature all <a href="#access-modes">access modes</a>
* (see {@link #ALL_ACCESS}).
* (see {@link #ALL_ACCESS}), and its confinement thread is the current thread (see {@link Thread#currentThread()}).
*
* @implNote The block of off-heap memory associated with the returned native memory segment is initialized to zero.
* Moreover, a client is responsible to call the {@link MemorySegment#close()} on a native memory segment,
@ -595,7 +873,7 @@ allocateNative(bytesSize, 1);
*
* @param bytesSize the size (in bytes) of the off-heap memory block backing the native memory segment.
* @param alignmentBytes the alignment constraint (in bytes) of the off-heap memory block backing the native memory segment.
* @return a new native memory segment.
* @return a new confined native memory segment.
* @throws IllegalArgumentException if {@code bytesSize < 0}, {@code alignmentBytes < 0}, or if {@code alignmentBytes}
* is not a power of 2.
*/
@ -613,39 +891,27 @@ allocateNative(bytesSize, 1);
}
/**
* Returns a new native memory segment with given base address and size; the returned segment has its own temporal
* bounds, and can therefore be closed; closing such a segment can optionally result in calling an user-provided cleanup
* action. This method can be very useful when interacting with custom native memory sources (e.g. custom allocators,
* GPU memory, etc.), where an address to some underlying memory region is typically obtained from native code
* (often as a plain {@code long} value). The segment will feature all <a href="#access-modes">access modes</a>
* (see {@link #ALL_ACCESS}).
* Returns a shared native memory segment whose base address is {@link MemoryAddress#NULL} and whose size is {@link Long#MAX_VALUE}.
* This method can be very useful when dereferencing memory addresses obtained when interacting with native libraries.
* The segment will feature the {@link #READ} and {@link #WRITE} <a href="#access-modes">access modes</a>.
* Equivalent to (but likely more efficient than) the following code:
* <pre>{@code
MemoryAddress.NULL.asSegmentRestricted(Long.MAX_VALUE)
.withOwnerThread(null)
.withAccessModes(READ | WRITE);
* }</pre>
* <p>
* This method is <em>restricted</em>. Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param addr the desired base address
* @param bytesSize the desired size.
* @param owner the desired owner thread. If {@code owner == null}, the returned segment is <em>not</em> confined.
* @param cleanup a cleanup action to be executed when the {@link MemorySegment#close()} method is called on the
* returned segment. If {@code cleanup == null}, no cleanup action is executed.
* @param attachment an object that must be kept alive by the returned segment; this can be useful when
* the returned segment depends on memory which could be released if a certain object
* is determined to be unreacheable. In most cases this will be set to {@code null}.
* @return a new native memory segment with given base address, size, owner, cleanup action and object attachment.
* @throws IllegalArgumentException if {@code bytesSize <= 0}.
* @throws UnsupportedOperationException if {@code addr} is associated with an heap segment.
* @return a memory segment whose base address is {@link MemoryAddress#NULL} and whose size is {@link Long#MAX_VALUE}.
* @throws IllegalAccessError if the runtime property {@code foreign.restricted} is not set to either
* {@code permit}, {@code warn} or {@code debug} (the default value is set to {@code deny}).
* @throws NullPointerException if {@code addr == null}.
*/
static MemorySegment ofNativeRestricted(MemoryAddress addr, long bytesSize, Thread owner, Runnable cleanup, Object attachment) {
Objects.requireNonNull(addr);
if (bytesSize <= 0) {
throw new IllegalArgumentException("Invalid size : " + bytesSize);
}
static MemorySegment ofNativeRestricted() {
Utils.checkRestrictedAccess("MemorySegment.ofNativeRestricted");
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, bytesSize, owner, cleanup, attachment);
return NativeMemorySegmentImpl.EVERYTHING;
}
// access mode masks
@ -672,25 +938,24 @@ allocateNative(bytesSize, 1);
int CLOSE = WRITE << 1;
/**
* Acquire access mode; this segment support sharing with threads other than the owner thread, via spliterator
* (see {@link #spliterator(MemorySegment, SequenceLayout)}).
* Share access mode; this segment support sharing with threads other than the owner thread (see {@link #share()}).
* @see MemorySegment#accessModes()
* @see MemorySegment#withAccessModes(int)
*/
int ACQUIRE = CLOSE << 1;
int SHARE = CLOSE << 1;
/**
* Handoff access mode; this segment support serial thread-confinement via thread ownership changes
* (see {@link #withOwnerThread(Thread)}).
* (see {@link #handoff(Thread)}).
* @see MemorySegment#accessModes()
* @see MemorySegment#withAccessModes(int)
*/
int HANDOFF = ACQUIRE << 1;
int HANDOFF = SHARE << 1;
/**
* Default access mode; this is a union of all the access modes supported by memory segments.
* @see MemorySegment#accessModes()
* @see MemorySegment#withAccessModes(int)
*/
int ALL_ACCESS = READ | WRITE | CLOSE | ACQUIRE | HANDOFF;
int ALL_ACCESS = READ | WRITE | CLOSE | SHARE | HANDOFF;
}

View File

@ -28,9 +28,12 @@
* <p> Classes to support low-level, safe and efficient memory access.
* <p>
* The key abstractions introduced by this package are {@link jdk.incubator.foreign.MemorySegment} and {@link jdk.incubator.foreign.MemoryAddress}.
* The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - which can
* sometimes be expressed as an offset into a given segment. A memory address represents the main access coordinate of a memory access var handle, which can be obtained
* using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class
* The first models a contiguous memory region, which can reside either inside or outside the Java heap; the latter models an address - which also can
* reside either inside or outside the Java heap (and can sometimes be expressed as an offset into a given segment).
* A memory segment represents the main access coordinate of a memory access var handle, which can be obtained
* using the combinator methods defined in the {@link jdk.incubator.foreign.MemoryHandles} class; a set of
* common dereference operations is provided also by the {@link jdk.incubator.foreign.MemoryAccess} class, which can
* be useful for simple, non-structured access. Finally, the {@link jdk.incubator.foreign.MemoryLayout} class
* hierarchy enables description of <em>memory layouts</em> and basic operations such as computing the size in bytes of a given
* layout, obtain its alignment requirements, and so on. Memory layouts also provide an alternate, more abstract way, to produce
* memory access var handles, e.g. using <a href="MemoryLayout.html#layout-paths"><em>layout paths</em></a>.
@ -39,25 +42,21 @@
* ranging from {@code 0} to {@code 9}, we can use the following code:
*
* <pre>{@code
static final VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
MemoryAddress base = segment.baseAddress();
for (long i = 0 ; i < 10 ; i++) {
intHandle.set(base.addOffset(i * 4), (int)i);
for (int i = 0 ; i < 10 ; i++) {
MemoryAccess.setIntAtIndex(segment, i);
}
}
* }</pre>
*
* Here we create a var handle, namely {@code intHandle}, to manipulate values of the primitive type {@code int}, at
* a given memory location. Also, {@code intHandle} is stored in a {@code static} and {@code final} field, to achieve
* better performance and allow for inlining of the memory access operation through the {@link java.lang.invoke.VarHandle}
* instance. We then create a <em>native</em> memory segment, that is, a memory segment backed by
* Here create a <em>native</em> memory segment, that is, a memory segment backed by
* off-heap memory; the size of the segment is 40 bytes, enough to store 10 values of the primitive type {@code int}.
* The segment is created inside a <em>try-with-resources</em> construct: this idiom ensures that all the memory resources
* associated with the segment will be released at the end of the block, according to the semantics described in
* Section {@jls 14.20.3} of <cite>The Java Language Specification</cite>. Inside the try-with-resources block, we initialize
* the contents of the memory segment; more specifically, if we view the memory segment as a set of 10 adjacent slots,
* the contents of the memory segment using the
* {@link jdk.incubator.foreign.MemoryAccess#setIntAtIndex(jdk.incubator.foreign.MemorySegment, long, int)} helper method;
* more specifically, if we view the memory segment as a set of 10 adjacent slots,
* {@code s[i]}, where {@code 0 <= i < 10}, where the size of each slot is exactly 4 bytes, the initialization logic above will set each slot
* so that {@code s[i] = i}, again where {@code 0 <= i < 10}.
*
@ -77,18 +76,18 @@ try (MemorySegment segment = MemorySegment.allocateNative(10 * 4)) {
*
* <h2><a id="safety"></a>Safety</h2>
*
* This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment using
* a memory address, such an address is validated (upon access), to make sure that it does not point to a memory location
* which resides <em>outside</em> the boundaries of the memory segment it refers to. We call this guarantee <em>spatial safety</em>;
* This API provides strong safety guarantees when it comes to memory access. First, when dereferencing a memory segment,
* the access coordinates are validated (upon access), to make sure that access does not occur at an address which resides
* <em>outside</em> the boundaries of the memory segment used by the dereference operation. We call this guarantee <em>spatial safety</em>;
* in other words, access to memory segments is bounds-checked, in the same way as array access is, as described in
* Section {@jls 15.10.4} of <cite>The Java Language Specification</cite>.
* <p>
* Since memory segments can be closed (see above), a memory address is also validated (upon access) to make sure that
* the segment it belongs to has not been closed prematurely. We call this guarantee <em>temporal safety</em>. Note that,
* Since memory segments can be closed (see above), segments are also validated (upon access) to make sure that
* the segment being accessed has not been closed prematurely. We call this guarantee <em>temporal safety</em>. Note that,
* in the general case, guaranteeing temporal safety can be hard, as multiple threads could attempt to access and/or close
* the same memory segment concurrently. The memory access API addresses this problem by imposing strong
* <a href="MemorySegment.html#thread-confinement"><em>thread-confinement</em></a> guarantees on memory segments: each
* memory segment is associated with an owner thread, which is the only thread that can either access or close the segment.
* <em>thread-confinement</em> guarantees on memory segments: upon creation, a memory segment is associated with an owner thread,
* which is the only thread that can either access or close the segment.
* <p>
* Together, spatial and temporal safety ensure that each memory access operation either succeeds - and accesses a valid
* memory location - or fails.

View File

@ -25,28 +25,24 @@
package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.*;
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.ScopedMemoryAccess;
import jdk.internal.util.ArraysSupport;
import jdk.internal.vm.annotation.ForceInline;
import sun.security.action.GetPropertyAction;
import java.io.FileDescriptor;
import java.lang.invoke.VarHandle;
import java.lang.ref.Cleaner;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.Spliterator;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
/**
* This abstract class provides an immutable implementation for the {@code MemorySegment} interface. This class contains information
@ -59,7 +55,7 @@ import java.util.function.Consumer;
*/
public abstract class AbstractMemorySegmentImpl implements MemorySegment, MemorySegmentProxy {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private static final ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
private static final boolean enableSmallSegments =
Boolean.parseBoolean(GetPropertyAction.privilegedGetProperty("jdk.incubator.foreign.SmallSegments", "true"));
@ -101,58 +97,62 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
return asSliceNoCheck(offset, newSize);
}
@Override
public AbstractMemorySegmentImpl asSlice(long offset) {
checkBounds(offset, 0);
return asSliceNoCheck(offset, length - offset);
}
private AbstractMemorySegmentImpl asSliceNoCheck(long offset, long newSize) {
return dup(offset, newSize, mask, scope);
}
@SuppressWarnings("unchecked")
public static <S extends MemorySegment> Spliterator<S> spliterator(S segment, SequenceLayout sequenceLayout) {
((AbstractMemorySegmentImpl)segment).checkValidState();
if (sequenceLayout.byteSize() != segment.byteSize()) {
@Override
public Spliterator<MemorySegment> spliterator(SequenceLayout sequenceLayout) {
checkValidState();
if (sequenceLayout.byteSize() != byteSize()) {
throw new IllegalArgumentException();
}
return (Spliterator<S>)new SegmentSplitter(sequenceLayout.elementLayout().byteSize(), sequenceLayout.elementCount().getAsLong(),
(AbstractMemorySegmentImpl)segment.withAccessModes(segment.accessModes() & ~CLOSE));
return new SegmentSplitter(sequenceLayout.elementLayout().byteSize(), sequenceLayout.elementCount().getAsLong(),
withAccessModes(accessModes() & ~CLOSE));
}
@Override
public final MemorySegment fill(byte value){
checkRange(0, length, true);
UNSAFE.setMemory(base(), min(), length, value);
checkAccess(0, length, false);
SCOPED_MEMORY_ACCESS.setMemory(scope, base(), min(), length, value);
return this;
}
public void copyFrom(MemorySegment src) {
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)src;
long size = that.byteSize();
checkRange(0, size, true);
that.checkRange(0, size, false);
UNSAFE.copyMemory(
checkAccess(0, size, false);
that.checkAccess(0, size, true);
SCOPED_MEMORY_ACCESS.copyMemory(scope, that.scope,
that.base(), that.min(),
base(), min(), size);
}
private final static VarHandle BYTE_HANDLE = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
.varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
@Override
public long mismatch(MemorySegment other) {
AbstractMemorySegmentImpl that = (AbstractMemorySegmentImpl)other;
final long thisSize = this.byteSize();
final long thatSize = that.byteSize();
final long length = Math.min(thisSize, thatSize);
this.checkRange(0, length, false);
that.checkRange(0, length, false);
this.checkAccess(0, length, true);
that.checkAccess(0, length, true);
if (this == other) {
checkValidState();
return -1;
}
long i = 0;
if (length > 7) {
if ((byte) BYTE_HANDLE.get(this.baseAddress(), 0) != (byte) BYTE_HANDLE.get(that.baseAddress(), 0)) {
if (MemoryAccess.getByte(this) != MemoryAccess.getByte(that)) {
return 0;
}
i = ArraysSupport.vectorizedMismatchLargeForBytes(
i = vectorizedMismatchLargeForBytes(scope, that.scope,
this.base(), this.min(),
that.base(), that.min(),
length);
@ -163,20 +163,51 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
assert remaining < 8 : "remaining greater than 7: " + remaining;
i = length - remaining;
}
MemoryAddress thisAddress = this.baseAddress();
MemoryAddress thatAddress = that.baseAddress();
for (; i < length; i++) {
if ((byte) BYTE_HANDLE.get(thisAddress, i) != (byte) BYTE_HANDLE.get(thatAddress, i)) {
if (MemoryAccess.getByteAtOffset(this, i) != MemoryAccess.getByteAtOffset(that, i)) {
return i;
}
}
return thisSize != thatSize ? length : -1;
}
/**
* Mismatch over long lengths.
*/
private static long vectorizedMismatchLargeForBytes(MemoryScope aScope, MemoryScope bScope,
Object a, long aOffset,
Object b, long bOffset,
long length) {
long off = 0;
long remaining = length;
int i, size;
boolean lastSubRange = false;
while (remaining > 7 && !lastSubRange) {
if (remaining > Integer.MAX_VALUE) {
size = Integer.MAX_VALUE;
} else {
size = (int) remaining;
lastSubRange = true;
}
i = SCOPED_MEMORY_ACCESS.vectorizedMismatch(aScope, bScope,
a, aOffset + off,
b, bOffset + off,
size, ArraysSupport.LOG2_ARRAY_BYTE_INDEX_SCALE);
if (i >= 0)
return off + i;
i = size - ~i;
off += i;
remaining -= i;
}
return ~remaining;
}
@Override
@ForceInline
public final MemoryAddress baseAddress() {
return new MemoryAddressImpl(this, 0);
public final MemoryAddress address() {
checkValidState();
return new MemoryAddressImpl(base(), min());
}
@Override
@ -184,7 +215,7 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
if (!isSet(READ)) {
throw unsupportedAccessMode(READ);
}
checkIntSize("ByteBuffer");
checkArraySize("ByteBuffer", 1);
ByteBuffer _bb = makeByteBuffer();
if (!isSet(WRITE)) {
//scope is IMMUTABLE - obtain a RO byte buffer
@ -234,69 +265,137 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
}
}
@Override
public MemorySegment withOwnerThread(Thread newOwner) {
Objects.requireNonNull(newOwner);
public MemorySegment handoff(Thread thread) {
Objects.requireNonNull(thread);
checkValidState();
if (!isSet(HANDOFF)) {
throw unsupportedAccessMode(HANDOFF);
}
if (scope.ownerThread() == newOwner) {
throw new IllegalArgumentException("Segment already owned by thread: " + newOwner);
} else {
try {
return dup(0L, length, mask, scope.dup(newOwner));
} finally {
//flush read/writes to segment memory before returning the new segment
VarHandle.fullFence();
}
try {
return dup(0L, length, mask, scope.confineTo(thread));
} finally {
//flush read/writes to segment memory before returning the new segment
VarHandle.fullFence();
}
}
@Override
public MemorySegment share() {
checkValidState();
if (!isSet(SHARE)) {
throw unsupportedAccessMode(SHARE);
}
try {
return dup(0L, length, mask, scope.share());
} finally {
//flush read/writes to segment memory before returning the new segment
VarHandle.fullFence();
}
}
@Override
public MemorySegment registerCleaner(Cleaner cleaner) {
Objects.requireNonNull(cleaner);
checkValidState();
if (!isSet(CLOSE)) {
throw unsupportedAccessMode(CLOSE);
}
return dup(0L, length, mask, scope.cleanable(cleaner));
}
@Override
public final void close() {
checkValidState();
if (!isSet(CLOSE)) {
throw unsupportedAccessMode(CLOSE);
}
closeNoCheck();
}
private final void closeNoCheck() {
scope.close();
}
final AbstractMemorySegmentImpl acquire() {
if (Thread.currentThread() != ownerThread() && !isSet(ACQUIRE)) {
throw unsupportedAccessMode(ACQUIRE);
}
return dup(0, length, mask, scope.acquire());
@Override
public boolean isMapped() {
return false;
}
@Override
public final byte[] toByteArray() {
checkIntSize("byte[]");
byte[] arr = new byte[(int)length];
MemorySegment arrSegment = MemorySegment.ofArray(arr);
return toArray(byte[].class, 1, byte[]::new, MemorySegment::ofArray);
}
@Override
public final short[] toShortArray() {
return toArray(short[].class, 2, short[]::new, MemorySegment::ofArray);
}
@Override
public final char[] toCharArray() {
return toArray(char[].class, 2, char[]::new, MemorySegment::ofArray);
}
@Override
public final int[] toIntArray() {
return toArray(int[].class, 4, int[]::new, MemorySegment::ofArray);
}
@Override
public final float[] toFloatArray() {
return toArray(float[].class, 4, float[]::new, MemorySegment::ofArray);
}
@Override
public final long[] toLongArray() {
return toArray(long[].class, 8, long[]::new, MemorySegment::ofArray);
}
@Override
public final double[] toDoubleArray() {
return toArray(double[].class, 8, double[]::new, MemorySegment::ofArray);
}
private <Z> Z toArray(Class<Z> arrayClass, int elemSize, IntFunction<Z> arrayFactory, Function<Z, MemorySegment> segmentFactory) {
int size = checkArraySize(arrayClass.getSimpleName(), elemSize);
Z arr = arrayFactory.apply(size);
MemorySegment arrSegment = segmentFactory.apply(arr);
arrSegment.copyFrom(this);
return arr;
}
boolean isSmall() {
@Override
public boolean isSmall() {
return isSet(SMALL);
}
void checkRange(long offset, long length, boolean writeAccess) {
scope.checkValidState();
if (writeAccess && !isSet(WRITE)) {
@Override
public void checkAccess(long offset, long length, boolean readOnly) {
if (!readOnly && !isSet(WRITE)) {
throw unsupportedAccessMode(WRITE);
} else if (!writeAccess && !isSet(READ)) {
} else if (readOnly && !isSet(READ)) {
throw unsupportedAccessMode(READ);
}
checkBounds(offset, length);
}
private void checkAccessAndScope(long offset, long length, boolean readOnly) {
checkValidState();
checkAccess(offset, length, readOnly);
}
private void checkValidState() {
try {
scope.checkValidState();
} catch (ScopedMemoryAccess.Scope.ScopedAccessError ex) {
throw new IllegalStateException("This segment is already closed");
}
}
@Override
public final void checkValidState() {
scope.checkValidState();
public long unsafeGetOffset() {
return min();
}
@Override
public Object unsafeGetBase() {
return base();
}
// Helper methods
@ -305,10 +404,15 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
return (this.mask & mask) != 0;
}
private void checkIntSize(String typeName) {
if (length > (Integer.MAX_VALUE - 8)) { //conservative check
private int checkArraySize(String typeName, int elemSize) {
if (length % elemSize != 0) {
throw new UnsupportedOperationException(String.format("Segment size is not a multiple of %d. Size: %d", elemSize, length));
}
long arraySize = length / elemSize;
if (arraySize > (Integer.MAX_VALUE - 8)) { //conservative check
throw new UnsupportedOperationException(String.format("Segment is too large to wrap as %s. Size: %d", typeName, length));
}
return (int)arraySize;
}
private void checkBounds(long offset, long length) {
@ -323,6 +427,11 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
}
}
@Override
public MemoryScope scope() {
return scope;
}
private void checkBoundsSmall(int offset, int length) {
if (length < 0 ||
offset < 0 ||
@ -347,8 +456,8 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
if ((mode & CLOSE) != 0) {
modes.add("CLOSE");
}
if ((mode & ACQUIRE) != 0) {
modes.add("ACQUIRE");
if ((mode & SHARE) != 0) {
modes.add("SHARE");
}
if ((mode & HANDOFF) != 0) {
modes.add("HANDOFF");
@ -398,11 +507,10 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
public boolean tryAdvance(Consumer<? super MemorySegment> action) {
Objects.requireNonNull(action);
if (currentIndex < elemCount) {
AbstractMemorySegmentImpl acquired = segment.acquire();
AbstractMemorySegmentImpl acquired = segment;
try {
action.accept(acquired.asSliceNoCheck(currentIndex * elementSize, elementSize));
} finally {
acquired.closeNoCheck();
currentIndex++;
if (currentIndex == elemCount) {
segment = null;
@ -418,7 +526,7 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
public void forEachRemaining(Consumer<? super MemorySegment> action) {
Objects.requireNonNull(action);
if (currentIndex < elemCount) {
AbstractMemorySegmentImpl acquired = segment.acquire();
AbstractMemorySegmentImpl acquired = segment;
try {
if (acquired.isSmall()) {
int index = (int) currentIndex;
@ -433,7 +541,6 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
}
}
} finally {
acquired.closeNoCheck();
currentIndex = elemCount;
segment = null;
}
@ -474,7 +581,7 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
bufferScope = bufferSegment.scope;
modes = bufferSegment.mask;
} else {
bufferScope = MemoryScope.create(bb, null);
bufferScope = MemoryScope.createConfined(bb, MemoryScope.DUMMY_CLEANUP_ACTION, null);
modes = defaultAccessModes(size);
}
if (bb.isReadOnly()) {
@ -488,28 +595,4 @@ public abstract class AbstractMemorySegmentImpl implements MemorySegment, Memory
return new MappedMemorySegmentImpl(bbAddress + pos, unmapper, size, modes, bufferScope);
}
}
public static final AbstractMemorySegmentImpl NOTHING = new AbstractMemorySegmentImpl(
0, 0, MemoryScope.createUnchecked(null, null, null)
) {
@Override
ByteBuffer makeByteBuffer() {
throw new UnsupportedOperationException();
}
@Override
long min() {
return 0;
}
@Override
Object base() {
return null;
}
@Override
AbstractMemorySegmentImpl dup(long offset, long size, int mask, MemoryScope scope) {
throw new UnsupportedOperationException();
}
};
}

View File

@ -121,7 +121,7 @@ public class HeapMemorySegmentImpl<H> extends AbstractMemorySegmentImpl {
static <Z> HeapMemorySegmentImpl<Z> makeHeapSegment(Supplier<Z> obj, int length, int base, int scale) {
int byteSize = length * scale;
MemoryScope scope = MemoryScope.create(null, null);
MemoryScope scope = MemoryScope.createConfined(null, MemoryScope.DUMMY_CLEANUP_ACTION, null);
return new HeapMemorySegmentImpl<>(base, obj, byteSize, defaultAccessModes(byteSize), scope);
}
}

View File

@ -25,21 +25,28 @@
*/
package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import sun.invoke.util.Wrapper;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout;
import sun.invoke.util.Wrapper;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;
import java.util.stream.LongStream;
/**
* This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout, ToLongFunction)},
@ -53,6 +60,17 @@ public class LayoutPath {
private static final JavaLangInvokeAccess JLI = SharedSecrets.getJavaLangInvokeAccess();
private static final MethodHandle ADD_STRIDE;
static {
try {
ADD_STRIDE = MethodHandles.lookup().findStatic(LayoutPath.class, "addStride",
MethodType.methodType(long.class, MemorySegment.class, long.class, long.class, long.class));
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
private final MemoryLayout layout;
private final long offset;
private final LayoutPath enclosing;
@ -141,12 +159,30 @@ public class LayoutPath {
checkAlignment(this);
return Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(
carrier,
layout.byteAlignment() - 1, //mask
((ValueLayout) layout).order(),
Utils.bitsToBytesOrThrow(offset, IllegalStateException::new),
LongStream.of(strides).map(s -> Utils.bitsToBytesOrThrow(s, IllegalStateException::new)).toArray()));
List<Class<?>> expectedCoordinates = new ArrayList<>();
Deque<Integer> perms = new ArrayDeque<>();
perms.addFirst(0);
expectedCoordinates.add(MemorySegment.class);
VarHandle handle = Utils.fixUpVarHandle(JLI.memoryAccessVarHandle(carrier, true, layout.byteAlignment() - 1,
((ValueLayout)layout).order()));
for (int i = 0 ; i < strides.length ; i++) {
expectedCoordinates.add(long.class);
perms.addFirst(0);
perms.addLast(i + 1);
//add stride
handle = MemoryHandles.collectCoordinates(handle, 1 + i,
MethodHandles.insertArguments(ADD_STRIDE, 1, Utils.bitsToBytesOrThrow(strides[strides.length - 1 - i], IllegalStateException::new))); // MS, long, MS_n, long_n, long
}
//add offset
handle = MemoryHandles.insertCoordinates(handle, 1 + strides.length, Utils.bitsToBytesOrThrow(offset, IllegalStateException::new));
if (strides.length > 0) {
// remove duplicate MS args
handle = MemoryHandles.permuteCoordinates(handle, expectedCoordinates, perms.stream().mapToInt(i -> i).toArray());
}
return handle;
}
public MemoryLayout layout() {
@ -284,4 +320,9 @@ public class LayoutPath {
return kind;
}
}
private static long addStride(MemorySegment segment, long stride, long base, long index) {
return MemorySegmentProxy.addOffsets(base,
MemorySegmentProxy.multiplyOffsets(stride, index, ((MemorySegmentProxy)segment)), (MemorySegmentProxy)segment);
}
}

View File

@ -25,18 +25,19 @@
package jdk.internal.foreign;
import jdk.incubator.foreign.MappedMemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.foreign.UnmapperProxy;
import jdk.internal.misc.ScopedMemoryAccess;
import sun.nio.ch.FileChannelImpl;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Optional;
/**
* Implementation for a mapped memory segments. A mapped memory segment is a native memory segment, which
@ -44,10 +45,12 @@ import java.nio.file.StandardOpenOption;
* memory mapped segment, such as the file descriptor associated with the mapping. This information is crucial
* in order to correctly reconstruct a byte buffer object from the segment (see {@link #makeByteBuffer()}).
*/
public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl implements MappedMemorySegment {
public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl {
private final UnmapperProxy unmapper;
static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
MappedMemorySegmentImpl(long min, UnmapperProxy unmapper, long length, int mask, MemoryScope scope) {
super(min, length, mask, scope);
this.unmapper = unmapper;
@ -55,7 +58,6 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl implements
@Override
ByteBuffer makeByteBuffer() {
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
return nioAccess.newMappedByteBuffer(unmapper, min, (int)length, null, this);
}
@ -78,33 +80,40 @@ public class MappedMemorySegmentImpl extends NativeMemorySegmentImpl implements
}
@Override
public boolean isMapped() {
return true;
}
// support for mapped segments
public MemorySegment segment() {
return MappedMemorySegmentImpl.this;
}
public void load() {
nioAccess.load(min, unmapper.isSync(), length);
SCOPED_MEMORY_ACCESS.load(scope, min, unmapper.isSync(), length);
}
@Override
public void unload() {
nioAccess.unload(min, unmapper.isSync(), length);
SCOPED_MEMORY_ACCESS.unload(scope, min, unmapper.isSync(), length);
}
@Override
public boolean isLoaded() {
return nioAccess.isLoaded(min, unmapper.isSync(), length);
return SCOPED_MEMORY_ACCESS.isLoaded(scope, min, unmapper.isSync(), length);
}
@Override
public void force() {
nioAccess.force(unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length);
SCOPED_MEMORY_ACCESS.force(scope, unmapper.fileDescriptor(), min, unmapper.isSync(), 0, length);
}
// factories
public static MappedMemorySegment makeMappedSegment(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
public static MemorySegment makeMappedSegment(Path path, long bytesOffset, long bytesSize, FileChannel.MapMode mapMode) throws IOException {
if (bytesSize < 0) throw new IllegalArgumentException("Requested bytes size must be >= 0.");
if (bytesOffset < 0) throw new IllegalArgumentException("Requested bytes offset must be >= 0.");
try (FileChannelImpl channelImpl = (FileChannelImpl)FileChannel.open(path, openOptions(mapMode))) {
UnmapperProxy unmapperProxy = channelImpl.mapInternal(mapMode, bytesOffset, bytesSize);
MemoryScope scope = MemoryScope.create(null, unmapperProxy::unmap);
MemoryScope scope = MemoryScope.createConfined(null, unmapperProxy::unmap, null);
int modes = defaultAccessModes(bytesSize);
if (mapMode == FileChannel.MapMode.READ_ONLY) {
modes &= ~WRITE;

View File

@ -25,9 +25,6 @@
*/
package jdk.internal.foreign;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.internal.misc.Unsafe;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
@ -37,93 +34,54 @@ import java.util.Objects;
* This class provides an immutable implementation for the {@code MemoryAddress} interface. This class contains information
* about the segment this address is associated with, as well as an offset into such segment.
*/
public final class MemoryAddressImpl implements MemoryAddress, MemoryAddressProxy {
public final class MemoryAddressImpl implements MemoryAddress {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
private final AbstractMemorySegmentImpl segment;
private final Object base;
private final long offset;
public MemoryAddressImpl(long offset) {
this.segment = AbstractMemorySegmentImpl.NOTHING;
this.offset = offset;
}
public MemoryAddressImpl(AbstractMemorySegmentImpl segment, long offset) {
this.segment = Objects.requireNonNull(segment);
public MemoryAddressImpl(Object base, long offset) {
this.base = base;
this.offset = offset;
}
// MemoryAddress methods
@Override
public long segmentOffset() {
if (segment() == null) {
throw new UnsupportedOperationException("Address does not have a segment");
public long segmentOffset(MemorySegment segment) {
Objects.requireNonNull(segment);
AbstractMemorySegmentImpl segmentImpl = (AbstractMemorySegmentImpl)segment;
if (segmentImpl.base() != base) {
throw new IllegalArgumentException("Invalid segment: " + segment);
}
return offset - segmentImpl.min();
}
@Override
public long toRawLongValue() {
if (base != null) {
throw new UnsupportedOperationException("Not a native address");
}
return offset;
}
@Override
public long toRawLongValue() {
if (unsafeGetBase() != null) {
throw new UnsupportedOperationException("Not a native address");
}
return unsafeGetOffset();
}
@Override
public MemorySegment segment() {
return segment != AbstractMemorySegmentImpl.NOTHING ?
segment : null;
}
@Override
public MemoryAddress addOffset(long bytes) {
return new MemoryAddressImpl(segment, offset + bytes);
return new MemoryAddressImpl(base, offset + bytes);
}
@Override
public MemoryAddress rebase(MemorySegment segment) {
AbstractMemorySegmentImpl segmentImpl = (AbstractMemorySegmentImpl)segment;
if (segmentImpl.base() != this.segment.base()) {
throw new IllegalArgumentException("Invalid rebase target: " + segment);
}
return new MemoryAddressImpl((AbstractMemorySegmentImpl)segment,
unsafeGetOffset() - ((MemoryAddressImpl)segment.baseAddress()).unsafeGetOffset());
}
// MemoryAddressProxy methods
public void checkAccess(long offset, long length, boolean readOnly) {
segment.checkRange(MemoryAddressProxy.addOffsets(this.offset, offset, this), length, !readOnly);
}
public long unsafeGetOffset() {
return segment.min() + offset;
}
public Object unsafeGetBase() {
return segment.base();
}
@Override
public boolean isSmall() {
return segment.isSmall();
}
// Object methods
@Override
public int hashCode() {
return Objects.hash(unsafeGetBase(), unsafeGetOffset());
return Objects.hash(base, offset);
}
@Override
public boolean equals(Object that) {
if (that instanceof MemoryAddressImpl) {
MemoryAddressImpl addr = (MemoryAddressImpl)that;
return Objects.equals(unsafeGetBase(), ((MemoryAddressImpl) that).unsafeGetBase()) &&
unsafeGetOffset() == addr.unsafeGetOffset();
return Objects.equals(base, addr.base) &&
offset == addr.offset;
} else {
return false;
}
@ -131,6 +89,15 @@ public final class MemoryAddressImpl implements MemoryAddress, MemoryAddressProx
@Override
public String toString() {
return "MemoryAddress{ region: " + segment + " offset=0x" + Long.toHexString(offset) + " }";
return "MemoryAddress{ base: " + base + " offset=0x" + Long.toHexString(offset) + " }";
}
@Override
public MemorySegment asSegmentRestricted(long bytesSize, Runnable cleanupAction, Object attachment) {
Utils.checkRestrictedAccess("MemoryAddress.asSegmentRestricted");
if (bytesSize <= 0) {
throw new IllegalArgumentException("Invalid size : " + bytesSize);
}
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(this, bytesSize, cleanupAction, attachment);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 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
@ -26,287 +26,282 @@
package jdk.internal.foreign;
import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.ref.PhantomCleanable;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.ref.Cleaner;
import java.lang.ref.Reference;
import java.util.Objects;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.StampedLock;
/**
* This class manages the temporal bounds associated with a memory segment as well
* as thread confinement.
* A scope has a liveness bit, which is updated when the scope is closed
* (this operation is triggered by {@link AbstractMemorySegmentImpl#close()}).
* A scope may also have an associated "owner" thread that confines some operations to
* associated owner thread such as {@link #close()} or {@link #dup(Thread)}.
* Furthermore, a scope is either root scope ({@link #create(Object, Runnable) created}
* when memory segment is allocated) or child scope ({@link #acquire() acquired} from root scope).
* When a child scope is acquired from another child scope, it is actually acquired from
* the root scope. There is only a single level of children. All child scopes are peers.
* A child scope can be {@link #close() closed} at any time, but root scope can only
* be closed after all its children have been closed, at which time any associated
* cleanup action is executed (the associated memory segment is freed).
* Besides thread-confined checked scopes, {@linkplain #createUnchecked(Thread, Object, Runnable)}
* method may be used passing {@code null} as the "owner" thread to create a
* scope that doesn't check for thread-confinement while its temporal bounds are
* enforced reliably only under condition that thread that closes the scope is also
* the single thread performing the checked access or there is an external synchronization
* in place that prevents concurrent access and closing of the scope.
* as thread confinement. A scope has a liveness bit, which is updated when the scope is closed
* (this operation is triggered by {@link AbstractMemorySegmentImpl#close()}). This bit is consulted prior
* to memory access (see {@link #checkValidState()}).
* There are two kinds of memory scope: confined memory scope and shared memory scope.
* A confined memory scope has an associated owner thread that confines some operations to
* associated owner thread such as {@link #close()} or {@link #checkValidState()}.
* Shared scopes do not feature an owner thread - meaning their operations can be called, in a racy
* manner, by multiple threads. To guarantee temporal safety in the presence of concurrent thread,
* shared scopes use a more sophisticated synchronization mechanism, which guarantees that no concurrent
* access is possible when a scope is being closed (see {@link jdk.internal.misc.ScopedMemoryAccess}).
*/
abstract class MemoryScope {
abstract class MemoryScope implements ScopedMemoryAccess.Scope {
/**
* Creates a root MemoryScope with given ref, cleanupAction and current
* thread as the "owner" thread.
* This method may be called in any thread.
* The returned instance may be published unsafely to and used in any thread,
* but methods that explicitly state that they may only be called in "owner" thread,
* must strictly be called in the thread that created the scope
* or else IllegalStateException is thrown.
*
* @param ref an optional reference to an instance that needs to be kept reachable
* @param cleanupAction an optional cleanup action to be executed when returned scope is closed
* @return a root MemoryScope
*/
static MemoryScope create(Object ref, Runnable cleanupAction) {
return new Root(Thread.currentThread(), ref, cleanupAction);
static final Runnable DUMMY_CLEANUP_ACTION = () -> { };
private MemoryScope(Object ref, Runnable cleanupAction, Cleaner cleaner) {
Objects.requireNonNull(cleanupAction);
this.ref = ref;
this.cleanupAction = cleanupAction;
this.scopeCleanable = cleaner != null ?
new ScopeCleanable(this, cleaner, cleanupAction) :
null;
}
/**
* Creates a root MemoryScope with given ref, cleanupAction and "owner" thread.
* This method may be called in any thread.
* The returned instance may be published unsafely to and used in any thread,
* but methods that explicitly state that they may only be called in "owner" thread,
* must strictly be called in given owner thread or else IllegalStateException is thrown.
* If given owner thread is null, the returned MemoryScope is unchecked, meaning
* that all methods may be called in any thread and that {@link #checkValidState()}
* does not check for temporal bounds.
*
* @param owner the desired owner thread. If {@code owner == null},
* the returned scope is <em>not</em> thread-confined and not checked.
* Creates a confined memory scope with given attachment and cleanup action. The returned scope
* is assumed to be confined on the current thread.
* @param ref an optional reference to an instance that needs to be kept reachable
* @param cleanupAction an optional cleanup action to be executed when returned scope is closed
* @return a root MemoryScope
* @param cleanupAction a cleanup action to be executed when returned scope is closed
* @return a confined memory scope
*/
static MemoryScope createUnchecked(Thread owner, Object ref, Runnable cleanupAction) {
return new Root(owner, ref, cleanupAction);
static MemoryScope createConfined(Object ref, Runnable cleanupAction, Cleaner cleaner) {
return new ConfinedScope(Thread.currentThread(), ref, cleanupAction, cleaner);
}
private final Thread owner;
private boolean closed; // = false
private static final VarHandle CLOSED;
/**
* Creates a shared memory scope with given attachment and cleanup action.
* @param ref an optional reference to an instance that needs to be kept reachable
* @param cleanupAction a cleanup action to be executed when returned scope is closed
* @return a shared memory scope
*/
static MemoryScope createShared(Object ref, Runnable cleanupAction, Cleaner cleaner) {
return new SharedScope(ref, cleanupAction, cleaner);
}
static {
protected final Object ref;
protected final ScopeCleanable scopeCleanable;
protected final Runnable cleanupAction;
/**
* Closes this scope, executing any cleanup action (where provided).
* @throws IllegalStateException if this scope is already closed or if this is
* a confined scope and this method is called outside of the owner thread.
*/
final void close() {
try {
CLOSED = MethodHandles.lookup().findVarHandle(MemoryScope.class, "closed", boolean.class);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
justClose();
cleanupAction.run();
if (scopeCleanable != null) {
scopeCleanable.clear();
}
} finally {
Reference.reachabilityFence(this);
}
}
private MemoryScope(Thread owner) {
this.owner = owner;
}
/**
* Acquires a child scope (or peer scope if this is a child) with current
* thread as the "owner" thread.
* This method may be called in any thread.
* The returned instance may be published unsafely to and used in any thread,
* but methods that explicitly state that they may only be called in "owner" thread,
* must strictly be called in the thread that acquired the scope
* or else IllegalStateException is thrown.
*
* @return a child (or peer) scope
* @throws IllegalStateException if root scope is already closed
*/
abstract MemoryScope acquire();
/**
* Closes this scope, executing any cleanup action if this is the root scope.
* This method may only be called in the "owner" thread of this scope unless the
* scope is a root scope with no owner thread - i.e. is not checked.
*
* @throws IllegalStateException if this scope is already closed or if this is
* a root scope and there is/are still active child
* scope(s) or if this method is called outside of
* owner thread in checked scope
*/
abstract void close();
abstract void justClose();
/**
* Duplicates this scope with given new "owner" thread and {@link #close() closes} it.
* If this is a root scope, a new root scope is returned; this root scope is closed, but
* without executing the cleanup action, which is instead transferred to the duped scope.
* If this is a child scope, a new child scope is returned.
* This method may only be called in the "owner" thread of this scope unless the
* scope is a root scope with no owner thread - i.e. is not checked.
* The returned instance may be published unsafely to and used in any thread,
* but methods that explicitly state that they may only be called in "owner" thread,
* must strictly be called in given new "owner" thread
* or else IllegalStateException is thrown.
*
* @param newOwner new owner thread of the returned MemoryScope
* @return a duplicate of this scope
* @throws NullPointerException if given owner thread is null
* @param newOwner new owner thread of the returned memory scope
* @return a new confined scope, which is a duplicate of this scope, but with a new owner thread.
* @throws IllegalStateException if this scope is already closed or if this is
* a root scope and there is/are still active child
* scope(s) or if this method is called outside of
* owner thread in checked scope
* a confined scope and this method is called outside of the owner thread.
*/
abstract MemoryScope dup(Thread newOwner);
final MemoryScope confineTo(Thread newOwner) {
try {
justClose();
if (scopeCleanable != null) {
scopeCleanable.clear();
}
return new ConfinedScope(newOwner, ref, cleanupAction, scopeCleanable != null ?
scopeCleanable.cleaner : null);
} finally {
Reference.reachabilityFence(this);
}
}
/**
* Duplicates this scope with given new "owner" thread and {@link #close() closes} it.
* @return a new shared scope, which is a duplicate of this scope.
* @throws IllegalStateException if this scope is already closed or if this is
* a confined scope and this method is called outside of the owner thread,
* or if this is already a shared scope.
*/
final MemoryScope share() {
try {
justClose();
if (scopeCleanable != null) {
scopeCleanable.clear();
}
return new SharedScope(ref, cleanupAction, scopeCleanable != null ?
scopeCleanable.cleaner : null);
} finally {
Reference.reachabilityFence(this);
}
}
final MemoryScope cleanable(Cleaner cleaner) {
if (scopeCleanable != null) {
throw new IllegalStateException("Already registered with a cleaner");
}
try {
justClose();
return ownerThread() == null ?
new SharedScope(ref, cleanupAction, cleaner) :
new ConfinedScope(ownerThread(), ref, cleanupAction, cleaner);
} finally {
Reference.reachabilityFence(this);
}
}
/**
* Returns "owner" thread of this scope.
*
* @return owner thread (or null for unchecked scope)
* @return owner thread (or null for a shared scope)
*/
final Thread ownerThread() {
return owner;
}
public abstract Thread ownerThread();
/**
* This method may be called in any thread.
*
* Returns true, if this scope is still alive. This method may be called in any thread.
* @return {@code true} if this scope is not closed yet.
*/
final boolean isAlive() {
return !((boolean)CLOSED.getVolatile(this));
public abstract boolean isAlive();
/**
* Checks that this scope is still alive (see {@link #isAlive()}).
* @throws IllegalStateException if this scope is already closed or if this is
* a confined scope and this method is called outside of the owner thread.
*/
public abstract void checkValidState();
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
/**
* Checks that this scope is still alive and that this method is executed
* in the "owner" thread of this scope or this scope is unchecked (not associated
* with owner thread).
*
* @throws IllegalStateException if this scope is already closed or this
* method is executed outside owning thread
* in checked scope
* A confined scope, which features an owner thread. The liveness check features an additional
* confinement check - that is, calling any operation on this scope from a thread other than the
* owner thread will result in an exception. Because of this restriction, checking the liveness bit
* can be performed in plain mode (see {@link #checkAliveRaw(MemoryScope)}).
*/
@ForceInline
final void checkValidState() {
if (owner != null && owner != Thread.currentThread()) {
throw new IllegalStateException("Attempted access outside owning thread");
static class ConfinedScope extends MemoryScope {
private boolean closed; // = false
final Thread owner;
public ConfinedScope(Thread owner, Object ref, Runnable cleanupAction, Cleaner cleaner) {
super(ref, cleanupAction, cleaner);
this.owner = owner;
}
@ForceInline
public final void checkValidState() {
if (owner != Thread.currentThread()) {
throw new IllegalStateException("Attempted access outside owning thread");
}
if (closed) {
throw ScopedAccessError.INSTANCE;
}
}
@Override
public boolean isAlive() {
return !closed;
}
void justClose() {
checkValidState();
closed = true;
}
@Override
public Thread ownerThread() {
return owner;
}
checkAliveConfined(this);
}
/**
* Checks that this scope is still alive.
*
* @throws IllegalStateException if this scope is already closed
* A shared scope, which can be shared across multiple threads. Closing a shared scope has to ensure that
* (i) only one thread can successfully close a scope (e.g. in a close vs. close race) and that
* (ii) no other thread is accessing the memory associated with this scope while the segment is being
* closed. To ensure the former condition, a CAS is performed on the liveness bit. Ensuring the latter
* is trickier, and require a complex synchronization protocol (see {@link jdk.internal.misc.ScopedMemoryAccess}).
* Since it is the responsibility of the closing thread to make sure that no concurrent access is possible,
* checking the liveness bit upon access can be performed in plain mode (see {@link #checkAliveRaw(MemoryScope)}),
* as in the confined case.
*/
@ForceInline
private static void checkAliveConfined(MemoryScope scope) {
if (scope.closed) {
throw new IllegalStateException("This segment is already closed");
static class SharedScope extends MemoryScope {
static ScopedMemoryAccess SCOPED_MEMORY_ACCESS = ScopedMemoryAccess.getScopedMemoryAccess();
final static int ALIVE = 0;
final static int CLOSING = 1;
final static int CLOSED = 2;
int state = ALIVE;
private static final VarHandle STATE;
static {
try {
STATE = MethodHandles.lookup().findVarHandle(SharedScope.class, "state", int.class);
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
SharedScope(Object ref, Runnable cleanupAction, Cleaner cleaner) {
super(ref, cleanupAction, cleaner);
}
@Override
public Thread ownerThread() {
return null;
}
@Override
public void checkValidState() {
if (state != ALIVE) {
throw ScopedAccessError.INSTANCE;
}
}
void justClose() {
if (!STATE.compareAndSet(this, ALIVE, CLOSING)) {
throw new IllegalStateException("Already closed");
}
boolean success = SCOPED_MEMORY_ACCESS.closeScope(this);
STATE.setVolatile(this, success ? CLOSED : ALIVE);
if (!success) {
throw new IllegalStateException("Cannot close while another thread is accessing the segment");
}
}
@Override
public boolean isAlive() {
return (int)STATE.getVolatile(this) != CLOSED;
}
}
private static final class Root extends MemoryScope {
private final StampedLock lock = new StampedLock();
private final LongAdder acquired = new LongAdder();
private final Object ref;
private final Runnable cleanupAction;
static class ScopeCleanable extends PhantomCleanable<MemoryScope> {
final Cleaner cleaner;
final Runnable cleanupAction;
private Root(Thread owner, Object ref, Runnable cleanupAction) {
super(owner);
this.ref = ref;
public ScopeCleanable(MemoryScope referent, Cleaner cleaner, Runnable cleanupAction) {
super(referent, cleaner);
this.cleaner = cleaner;
this.cleanupAction = cleanupAction;
}
@Override
MemoryScope acquire() {
// try to optimistically acquire the lock
long stamp = lock.tryOptimisticRead();
try {
for (; ; stamp = lock.readLock()) {
if (stamp == 0L)
continue;
checkAliveConfined(this); // plain read is enough here (either successful optimistic read, or full read lock)
// increment acquires
acquired.increment();
// did a call to close() occur since we acquired the lock?
if (lock.validate(stamp)) {
// no, just return the acquired scope
return new Child(Thread.currentThread());
} else {
// yes, just back off and retry (close might have failed, after all)
acquired.decrement();
}
}
} finally {
if (StampedLock.isReadLockStamp(stamp))
lock.unlockRead(stamp);
}
}
@Override
MemoryScope dup(Thread newOwner) {
Objects.requireNonNull(newOwner, "newOwner");
// pre-allocate duped scope so we don't get OOME later and be left with this scope closed
var duped = new Root(newOwner, ref, cleanupAction);
justClose();
return duped;
}
@Override
void close() {
justClose();
if (cleanupAction != null) {
cleanupAction.run();
}
}
@ForceInline
private void justClose() {
// enter critical section - no acquires are possible past this point
long stamp = lock.writeLock();
try {
checkValidState(); // plain read is enough here (full write lock)
// check for absence of active acquired children
if (acquired.sum() > 0) {
throw new IllegalStateException("Cannot close this scope as it has active acquired children");
}
// now that we made sure there's no active acquired children, we can mark scope as closed
CLOSED.set(this, true); // plain write is enough here (full write lock)
} finally {
// leave critical section
lock.unlockWrite(stamp);
}
}
private final class Child extends MemoryScope {
private Child(Thread owner) {
super(owner);
}
@Override
MemoryScope acquire() {
return Root.this.acquire();
}
@Override
MemoryScope dup(Thread newOwner) {
checkValidState(); // child scope is always checked
// pre-allocate duped scope so we don't get OOME later and be left with this scope closed
var duped = new Child(newOwner);
CLOSED.setVolatile(this, true);
return duped;
}
@Override
void close() {
checkValidState(); // child scope is always checked
CLOSED.set(this, true);
// following acts as a volatile write after plain write above so
// plain write gets flushed too (which is important for isAliveThreadSafe())
Root.this.acquired.decrement();
}
protected void performCleanup() {
cleanupAction.run();
}
}
}

View File

@ -28,9 +28,8 @@ package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.ForceInline;
import sun.security.action.GetBooleanAction;
@ -42,11 +41,15 @@ import java.nio.ByteBuffer;
*/
public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
public static final MemorySegment EVERYTHING = makeNativeSegmentUnchecked(MemoryAddress.NULL, Long.MAX_VALUE, MemoryScope.DUMMY_CLEANUP_ACTION, null)
.share()
.withAccessModes(READ | WRITE);
private static final Unsafe unsafe = Unsafe.getUnsafe();
// The maximum alignment supported by malloc - typically 16 on
// 64-bit platforms and 8 on 32-bit platforms.
private final static long MAX_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16;
private final static long MAX_MALLOC_ALIGN = Unsafe.ADDRESS_SIZE == 4 ? 8 : 16;
private static final boolean skipZeroMemory = GetBooleanAction.privilegedGetProperty("jdk.internal.foreign.skipZeroMemory");
@ -65,7 +68,6 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
@Override
ByteBuffer makeByteBuffer() {
JavaNioAccess nioAccess = SharedSecrets.getJavaNioAccess();
return nioAccess.newDirectByteBuffer(min(), (int) this.length, null, this);
}
@ -82,18 +84,24 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
// factories
public static MemorySegment makeNativeSegment(long bytesSize, long alignmentBytes) {
long alignedSize = bytesSize;
if (alignmentBytes > MAX_ALIGN) {
alignedSize = bytesSize + (alignmentBytes - 1);
if (VM.isDirectMemoryPageAligned()) {
alignmentBytes = Math.max(alignmentBytes, nioAccess.pageSize());
}
long alignedSize = alignmentBytes > MAX_MALLOC_ALIGN ?
bytesSize + (alignmentBytes - 1) :
bytesSize;
nioAccess.reserveMemory(alignedSize, bytesSize);
long buf = unsafe.allocateMemory(alignedSize);
if (!skipZeroMemory) {
unsafe.setMemory(buf, alignedSize, (byte)0);
}
long alignedBuf = Utils.alignUp(buf, alignmentBytes);
MemoryScope scope = MemoryScope.create(null, () -> unsafe.freeMemory(buf));
MemoryScope scope = MemoryScope.createConfined(null, () -> {
unsafe.freeMemory(buf);
nioAccess.unreserveMemory(alignedSize, bytesSize);
}, null);
MemorySegment segment = new NativeMemorySegmentImpl(buf, alignedSize,
defaultAccessModes(alignedSize), scope);
if (alignedSize != bytesSize) {
@ -103,8 +111,8 @@ public class NativeMemorySegmentImpl extends AbstractMemorySegmentImpl {
return segment;
}
public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, Thread owner, Runnable cleanup, Object attachment) {
MemoryScope scope = MemoryScope.createUnchecked(owner, attachment, cleanup);
return new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, defaultAccessModes(bytesSize), scope);
public static MemorySegment makeNativeSegmentUnchecked(MemoryAddress min, long bytesSize, Runnable cleanupAction, Object ref) {
return new NativeMemorySegmentImpl(min.toRawLongValue(), bytesSize, defaultAccessModes(bytesSize),
MemoryScope.createConfined(ref, cleanupAction == null ? MemoryScope.DUMMY_CLEANUP_ACTION : cleanupAction, null));
}
}

View File

@ -26,9 +26,9 @@
package jdk.internal.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.internal.access.foreign.MemoryAddressProxy;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.access.foreign.MemorySegmentProxy;
import jdk.internal.misc.VM;
import java.lang.invoke.MethodHandle;
@ -46,12 +46,12 @@ public final class Utils {
private static final String foreignRestrictedAccess = Optional.ofNullable(VM.getSavedProperty("foreign.restricted"))
.orElse("deny");
private static final MethodHandle ADDRESS_FILTER;
private static final MethodHandle SEGMENT_FILTER;
static {
try {
ADDRESS_FILTER = MethodHandles.lookup().findStatic(Utils.class, "filterAddress",
MethodType.methodType(MemoryAddressProxy.class, MemoryAddress.class));
SEGMENT_FILTER = MethodHandles.lookup().findStatic(Utils.class, "filterSegment",
MethodType.methodType(MemorySegmentProxy.class, MemorySegment.class));
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
@ -70,13 +70,13 @@ public final class Utils {
}
public static VarHandle fixUpVarHandle(VarHandle handle) {
// This adaptation is required, otherwise the memory access var handle will have type MemoryAddressProxy,
// and not MemoryAddress (which the user expects), which causes performance issues with asType() adaptations.
return MemoryHandles.filterCoordinates(handle, 0, ADDRESS_FILTER);
// This adaptation is required, otherwise the memory access var handle will have type MemorySegmentProxy,
// and not MemorySegment (which the user expects), which causes performance issues with asType() adaptations.
return MemoryHandles.filterCoordinates(handle, 0, SEGMENT_FILTER);
}
private static MemoryAddressProxy filterAddress(MemoryAddress addr) {
return (MemoryAddressImpl)addr;
private static MemorySegmentProxy filterSegment(MemorySegment segment) {
return (AbstractMemorySegmentImpl)segment;
}
public static void checkRestrictedAccess(String method) {

View File

@ -559,7 +559,6 @@ java/beans/XMLEncoder/Test6570354.java 8015593 macosx-all
# jdk_foreign
java/foreign/TestMismatch.java 8249684 macosx-all
java/foreign/TestMismatch.java 8255270 generic-i586
############################################################################

View File

@ -33,6 +33,7 @@
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.ValueLayout;
@ -70,7 +71,7 @@ public class TestAdaptVarHandles {
I2O = MethodHandles.explicitCastArguments(I2S, MethodType.methodType(Object.class, int.class));
S2L = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLong", MethodType.methodType(long.class, String.class));
S2L_EX = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "stringToLongException", MethodType.methodType(long.class, String.class));
BASE_ADDR = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "baseAddress", MethodType.methodType(MemoryAddress.class, MemorySegment.class));
BASE_ADDR = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "baseAddress", MethodType.methodType(MemorySegment.class, MemorySegment.class));
SUM_OFFSETS = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "sumOffsets", MethodType.methodType(long.class, long.class, long.class));
VOID_FILTER = MethodHandles.lookup().findStatic(TestAdaptVarHandles.class, "void_filter", MethodType.methodType(void.class, String.class));
@ -86,22 +87,29 @@ public class TestAdaptVarHandles {
}
}
static final VarHandle intHandleIndexed = MemoryLayout.ofSequence(MemoryLayouts.JAVA_INT)
.varHandle(int.class, MemoryLayout.PathElement.sequenceElement());
static final VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
static final VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class);
@Test
public void testFilterValue() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = layout.varHandle(int.class);
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, S2I, I2S);
i2SHandle.set(segment.baseAddress(), "1");
String oldValue = (String)i2SHandle.getAndAdd(segment.baseAddress(), "42");
i2SHandle.set(segment, "1");
String oldValue = (String)i2SHandle.getAndAdd(segment, "42");
assertEquals(oldValue, "1");
String value = (String)i2SHandle.get(segment.baseAddress());
String value = (String)i2SHandle.get(segment);
assertEquals(value, "43");
boolean swapped = (boolean)i2SHandle.compareAndSet(segment.baseAddress(), "43", "12");
boolean swapped = (boolean)i2SHandle.compareAndSet(segment, "43", "12");
assertTrue(swapped);
oldValue = (String)i2SHandle.compareAndExchange(segment.baseAddress(), "12", "42");
oldValue = (String)i2SHandle.compareAndExchange(segment, "12", "42");
assertEquals(oldValue, "12");
value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress());
value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment);
assertEquals(value, "42");
}
@ -113,16 +121,16 @@ public class TestAdaptVarHandles {
MethodHandle CTX_S2I = MethodHandles.dropArguments(S2I, 0, String.class, String.class);
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, CTX_S2I, CTX_I2S);
i2SHandle = MemoryHandles.insertCoordinates(i2SHandle, 1, "a", "b");
i2SHandle.set(segment.baseAddress(), "1");
String oldValue = (String)i2SHandle.getAndAdd(segment.baseAddress(), "42");
i2SHandle.set(segment, "1");
String oldValue = (String)i2SHandle.getAndAdd(segment, "42");
assertEquals(oldValue, "ab1");
String value = (String)i2SHandle.get(segment.baseAddress());
String value = (String)i2SHandle.get(segment);
assertEquals(value, "ab43");
boolean swapped = (boolean)i2SHandle.compareAndSet(segment.baseAddress(), "43", "12");
boolean swapped = (boolean)i2SHandle.compareAndSet(segment, "43", "12");
assertTrue(swapped);
oldValue = (String)i2SHandle.compareAndExchange(segment.baseAddress(), "12", "42");
oldValue = (String)i2SHandle.compareAndExchange(segment, "12", "42");
assertEquals(oldValue, "ab12");
value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress());
value = (String)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment);
assertEquals(value, "ab42");
}
@ -132,16 +140,16 @@ public class TestAdaptVarHandles {
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = layout.varHandle(int.class);
VarHandle i2SHandle = MemoryHandles.filterValue(intHandle, O2I, I2O);
i2SHandle.set(segment.baseAddress(), "1");
String oldValue = (String)i2SHandle.getAndAdd(segment.baseAddress(), "42");
i2SHandle.set(segment, "1");
String oldValue = (String)i2SHandle.getAndAdd(segment, "42");
assertEquals(oldValue, "1");
String value = (String)i2SHandle.get(segment.baseAddress());
String value = (String)i2SHandle.get(segment);
assertEquals(value, "43");
boolean swapped = (boolean)i2SHandle.compareAndSet(segment.baseAddress(), "43", "12");
boolean swapped = (boolean)i2SHandle.compareAndSet(segment, "43", "12");
assertTrue(swapped);
oldValue = (String)i2SHandle.compareAndExchange(segment.baseAddress(), "12", "42");
oldValue = (String)i2SHandle.compareAndExchange(segment, "12", "42");
assertEquals(oldValue, "12");
value = (String)(Object)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress());
value = (String)(Object)i2SHandle.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment);
assertEquals(value, "42");
}
@ -152,19 +160,16 @@ public class TestAdaptVarHandles {
@Test(expectedExceptions = NullPointerException.class)
public void testBadFilterNullUnbox() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(intHandle, null, I2S);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadFilterNullBox() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterValue(intHandle, S2I, null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCarrier() {
VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class);
MemoryHandles.filterValue(floatHandle, S2I, I2S);
}
@ -216,8 +221,7 @@ public class TestAdaptVarHandles {
public void testFilterCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_longIndex = MemoryHandles.filterCoordinates(intHandle, 0, BASE_ADDR, S2L);
VarHandle intHandle_longIndex = MemoryHandles.filterCoordinates(intHandleIndexed, 0, BASE_ADDR, S2L);
intHandle_longIndex.set(segment, "0", 1);
int oldValue = (int)intHandle_longIndex.getAndAdd(segment, "0", 42);
assertEquals(oldValue, 1);
@ -238,46 +242,39 @@ public class TestAdaptVarHandles {
@Test(expectedExceptions = NullPointerException.class)
public void testBadFilterCoordinatesNullFilters() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterCoordinates(intHandle, 0, null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesNegativePos() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterCoordinates(intHandle, -1, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesPosTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.filterCoordinates(intHandle, 1, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesWrongFilterType() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.filterCoordinates(intHandle, 1, S2I);
MemoryHandles.filterCoordinates(intHandleIndexed, 1, S2I);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesWrongFilterException() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.filterCoordinates(intHandle, 1, S2L_EX);
MemoryHandles.filterCoordinates(intHandleIndexed, 1, S2L_EX);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadFilterCoordinatesTooManyFilters() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.filterCoordinates(intHandle, 1, S2L, S2L);
MemoryHandles.filterCoordinates(intHandleIndexed, 1, S2L, S2L);
}
@Test
public void testInsertCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_longIndex = MemoryHandles.insertCoordinates(intHandle, 0, segment.baseAddress(), 0L);
VarHandle intHandle_longIndex = MemoryHandles.insertCoordinates(intHandleIndexed, 0, segment, 0L);
intHandle_longIndex.set(1);
int oldValue = (int)intHandle_longIndex.getAndAdd(42);
assertEquals(oldValue, 1);
@ -298,51 +295,45 @@ public class TestAdaptVarHandles {
@Test(expectedExceptions = NullPointerException.class)
public void testBadInsertCoordinatesNullValues() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.insertCoordinates(intHandle, 0, null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadInsertCoordinatesNegativePos() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.insertCoordinates(intHandle, -1, 42);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadInsertCoordinatesPosTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.insertCoordinates(intHandle, 1, 42);
}
@Test(expectedExceptions = ClassCastException.class)
public void testBadInsertCoordinatesWrongCoordinateType() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.insertCoordinates(intHandle, 1, "Hello");
MemoryHandles.insertCoordinates(intHandleIndexed, 1, "Hello");
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadInsertCoordinatesTooManyValues() {
VarHandle intHandle = MemoryHandles.withStride(MemoryLayouts.JAVA_INT.varHandle(int.class), 4);
MemoryHandles.insertCoordinates(intHandle, 1, 0L, 0L);
MemoryHandles.insertCoordinates(intHandleIndexed, 1, 0L, 0L);
}
@Test
public void testPermuteCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_swap = MemoryHandles.permuteCoordinates(intHandle,
List.of(long.class, MemoryAddress.class), 1, 0);
intHandle_swap.set(0L, segment.baseAddress(), 1);
int oldValue = (int)intHandle_swap.getAndAdd(0L, segment.baseAddress(), 42);
VarHandle intHandle_swap = MemoryHandles.permuteCoordinates(intHandleIndexed,
List.of(long.class, MemorySegment.class), 1, 0);
intHandle_swap.set(0L, segment, 1);
int oldValue = (int)intHandle_swap.getAndAdd(0L, segment, 42);
assertEquals(oldValue, 1);
int value = (int)intHandle_swap.get(0L, segment.baseAddress());
int value = (int)intHandle_swap.get(0L, segment);
assertEquals(value, 43);
boolean swapped = (boolean)intHandle_swap.compareAndSet(0L, segment.baseAddress(), 43, 12);
boolean swapped = (boolean)intHandle_swap.compareAndSet(0L, segment, 43, 12);
assertTrue(swapped);
oldValue = (int)intHandle_swap.compareAndExchange(0L, segment.baseAddress(), 12, 42);
oldValue = (int)intHandle_swap.compareAndExchange(0L, segment, 12, 42);
assertEquals(oldValue, 12);
value = (int)intHandle_swap.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(0L, segment.baseAddress());
value = (int)intHandle_swap.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(0L, segment);
assertEquals(value, 42);
}
@ -353,37 +344,31 @@ public class TestAdaptVarHandles {
@Test(expectedExceptions = NullPointerException.class)
public void testBadPermuteCoordinatesNullCoordinates() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, null);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadPermuteCoordinatesNullReorder() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of(int.class), null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadPermuteCoordinatesTooManyCoordinates() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), new int[2]);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadPermuteCoordinatesTooFewCoordinates() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadPermuteCoordinatesIndexTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), 3);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadPermuteCoordinatesIndexTooSmall() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.permuteCoordinates(intHandle, List.of(int.class, int.class), -1);
}
@ -391,18 +376,17 @@ public class TestAdaptVarHandles {
public void testCollectCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_sum = MemoryHandles.collectCoordinates(intHandle, 1, SUM_OFFSETS);
intHandle_sum.set(segment.baseAddress(), -2L, 2L, 1);
int oldValue = (int)intHandle_sum.getAndAdd(segment.baseAddress(), -2L, 2L, 42);
VarHandle intHandle_sum = MemoryHandles.collectCoordinates(intHandleIndexed, 1, SUM_OFFSETS);
intHandle_sum.set(segment, -2L, 2L, 1);
int oldValue = (int)intHandle_sum.getAndAdd(segment, -2L, 2L, 42);
assertEquals(oldValue, 1);
int value = (int)intHandle_sum.get(segment.baseAddress(), -2L, 2L);
int value = (int)intHandle_sum.get(segment, -2L, 2L);
assertEquals(value, 43);
boolean swapped = (boolean)intHandle_sum.compareAndSet(segment.baseAddress(), -2L, 2L, 43, 12);
boolean swapped = (boolean)intHandle_sum.compareAndSet(segment, -2L, 2L, 43, 12);
assertTrue(swapped);
oldValue = (int)intHandle_sum.compareAndExchange(segment.baseAddress(), -2L, 2L, 12, 42);
oldValue = (int)intHandle_sum.compareAndExchange(segment, -2L, 2L, 12, 42);
assertEquals(oldValue, 12);
value = (int)intHandle_sum.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress(), -2L, 2L);
value = (int)intHandle_sum.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, -2L, 2L);
assertEquals(value, 42);
}
@ -413,37 +397,31 @@ public class TestAdaptVarHandles {
@Test(expectedExceptions = NullPointerException.class)
public void testBadCollectCoordinatesNullFilters() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 0, null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesNegativePos() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, -1, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesPosTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 1, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesWrongFilterType() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 0, SUM_OFFSETS);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesWrongVoidFilterType() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 0, VOID_FILTER);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadCollectCoordinatesWrongFilterException() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.collectCoordinates(intHandle, 0, S2L_EX);
}
@ -451,36 +429,32 @@ public class TestAdaptVarHandles {
public void testDropCoordinates() throws Throwable {
ValueLayout layout = MemoryLayouts.JAVA_INT;
MemorySegment segment = MemorySegment.allocateNative(layout);
VarHandle intHandle = MemoryHandles.withStride(layout.varHandle(int.class), 4);
VarHandle intHandle_dummy = MemoryHandles.dropCoordinates(intHandle, 1, float.class, String.class);
intHandle_dummy.set(segment.baseAddress(), 1f, "hello", 0L, 1);
int oldValue = (int)intHandle_dummy.getAndAdd(segment.baseAddress(), 1f, "hello", 0L, 42);
VarHandle intHandle_dummy = MemoryHandles.dropCoordinates(intHandleIndexed, 1, float.class, String.class);
intHandle_dummy.set(segment, 1f, "hello", 0L, 1);
int oldValue = (int)intHandle_dummy.getAndAdd(segment, 1f, "hello", 0L, 42);
assertEquals(oldValue, 1);
int value = (int)intHandle_dummy.get(segment.baseAddress(), 1f, "hello", 0L);
int value = (int)intHandle_dummy.get(segment, 1f, "hello", 0L);
assertEquals(value, 43);
boolean swapped = (boolean)intHandle_dummy.compareAndSet(segment.baseAddress(), 1f, "hello", 0L, 43, 12);
boolean swapped = (boolean)intHandle_dummy.compareAndSet(segment, 1f, "hello", 0L, 43, 12);
assertTrue(swapped);
oldValue = (int)intHandle_dummy.compareAndExchange(segment.baseAddress(), 1f, "hello", 0L, 12, 42);
oldValue = (int)intHandle_dummy.compareAndExchange(segment, 1f, "hello", 0L, 12, 42);
assertEquals(oldValue, 12);
value = (int)intHandle_dummy.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment.baseAddress(), 1f, "hello", 0L);
value = (int)intHandle_dummy.toMethodHandle(VarHandle.AccessMode.GET).invokeExact(segment, 1f, "hello", 0L);
assertEquals(value, 42);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadDropCoordinatesNegativePos() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.dropCoordinates(intHandle, -1);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadDropCoordinatesPosTooBig() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.dropCoordinates(intHandle, 2);
}
@Test(expectedExceptions = NullPointerException.class)
public void testBadDropCoordinatesNullValueTypes() {
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
MemoryHandles.dropCoordinates(intHandle, 1, null);
}
@ -507,8 +481,8 @@ public class TestAdaptVarHandles {
return Long.valueOf(s);
}
static MemoryAddress baseAddress(MemorySegment segment) {
return segment.baseAddress();
static MemorySegment baseAddress(MemorySegment segment) {
return segment;
}
static long sumOffsets(long l1, long l2) {

View File

@ -61,58 +61,52 @@ public class TestAddressHandle {
@Test(dataProvider = "addressHandles")
public void testAddressHandle(VarHandle addrHandle, int byteSize) {
VarHandle longHandle = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class);
try (MemorySegment segment = MemorySegment.allocateNative(8)) {
MemoryAddress target = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ?
segment.baseAddress().addOffset(8 - byteSize) :
segment.baseAddress();
longHandle.set(segment.baseAddress(), 42L);
MemorySegment target = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN ?
segment.asSlice(8 - byteSize) :
segment;
longHandle.set(segment, 42L);
MemoryAddress address = (MemoryAddress)addrHandle.get(target);
assertEquals(address.toRawLongValue(), 42L);
try {
longHandle.get(address); // check that address cannot be de-referenced
fail();
} catch (UnsupportedOperationException ex) {
assertTrue(true);
}
addrHandle.set(target, address.addOffset(1));
long result = (long)longHandle.get(segment.baseAddress());
long result = (long)longHandle.get(segment);
assertEquals(43L, result);
}
}
@Test(dataProvider = "addressHandles")
public void testNull(VarHandle addrHandle, int byteSize) {
VarHandle longHandle = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class);
try (MemorySegment segment = MemorySegment.allocateNative(8)) {
longHandle.set(segment.baseAddress(), 0L);
MemoryAddress address = (MemoryAddress)addrHandle.get(segment.baseAddress());
longHandle.set(segment, 0L);
MemoryAddress address = (MemoryAddress)addrHandle.get(segment);
assertTrue(address == MemoryAddress.NULL);
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadAdaptFloat() {
VarHandle floatHandle = MemoryHandles.varHandle(float.class, ByteOrder.nativeOrder());
VarHandle floatHandle = MemoryLayouts.JAVA_FLOAT.varHandle(float.class);
MemoryHandles.asAddressVarHandle(floatHandle);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadAdaptDouble() {
VarHandle doubleHandle = MemoryHandles.varHandle(double.class, ByteOrder.nativeOrder());
VarHandle doubleHandle = MemoryLayouts.JAVA_DOUBLE.varHandle(double.class);
MemoryHandles.asAddressVarHandle(doubleHandle);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadAdaptBoolean() {
VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
VarHandle boolHandle = MemoryHandles.filterValue(intHandle, BOOL_TO_INT, INT_TO_BOOL);
MemoryHandles.asAddressVarHandle(boolHandle);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadAdaptString() {
VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
VarHandle intHandle = MemoryLayouts.JAVA_INT.varHandle(int.class);
VarHandle stringHandle = MemoryHandles.filterValue(intHandle, STRING_TO_INT, INT_TO_STRING);
MemoryHandles.asAddressVarHandle(stringHandle);
}
@ -121,32 +115,31 @@ public class TestAddressHandle {
static Object[][] addressHandles() {
return new Object[][] {
// long
{ MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder())), 8 },
{ MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()), 0)), 8 },
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder()), 0)), 8 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_LONG.varHandle(long.class)), 8 },
// int
{ MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder())), 4 },
{ MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 0)), 4 },
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 0)), 4 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_INT.varHandle(int.class)), 4 },
// short
{ MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder())), 2 },
{ MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder()), 0)), 2 },
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(short.class, ByteOrder.nativeOrder()), 0)), 2 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_SHORT.varHandle(short.class)), 2 },
// char
{ MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder())), 2 },
{ MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder()), 0)), 2 },
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(char.class, ByteOrder.nativeOrder()), 0)), 2 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_CHAR.varHandle(char.class)), 2 },
// byte
{ MemoryHandles.asAddressVarHandle(MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder())), 1 },
{ MemoryHandles.asAddressVarHandle(MemoryHandles.withOffset(MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()), 0)), 1 },
{ MemoryHandles.asAddressVarHandle(at(MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder()), 0)), 1 },
{ MemoryHandles.asAddressVarHandle(MemoryLayouts.JAVA_BYTE.varHandle(byte.class)), 1 }
};
}
static VarHandle at(VarHandle handle, long offset) {
return MemoryHandles.insertCoordinates(handle, 1, offset);
}
static int boolToInt(boolean value) {
return value ? 1 : 0;
}

View File

@ -36,7 +36,9 @@ import jdk.incubator.foreign.SequenceLayout;
import java.lang.invoke.VarHandle;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import org.testng.annotations.*;
@ -81,83 +83,126 @@ public class TestArrays {
static VarHandle longHandle = longs.varHandle(long.class, PathElement.sequenceElement());
static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement());
static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer<MemorySegment, Long> handleSetter) {
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
handleSetter.accept(base, i);
}
}
static void checkBytes(MemoryAddress base, SequenceLayout layout) {
long nBytes = layout.elementCount().getAsLong() * layout.elementLayout().byteSize();
byte[] arr = base.segment().toByteArray();
for (long i = 0 ; i < nBytes ; i++) {
byte expected = (byte)byteHandle.get(base, i);
byte found = arr[(int)i];
static void checkBytes(MemorySegment base, SequenceLayout layout, Function<MemorySegment, Object> arrayFactory, BiFunction<MemorySegment, Long, Object> handleGetter) {
int nelems = (int)layout.elementCount().getAsLong();
Object arr = arrayFactory.apply(base);
for (int i = 0; i < nelems; i++) {
Object found = handleGetter.apply(base, (long) i);
Object expected = java.lang.reflect.Array.get(arr, i);
assertEquals(expected, found);
}
}
@Test(dataProvider = "arrays")
public void testArrays(Consumer<MemoryAddress> init, SequenceLayout layout) {
public void testArrays(Consumer<MemorySegment> init, Consumer<MemorySegment> checker, MemoryLayout layout) {
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
init.accept(segment.baseAddress());
checkBytes(segment.baseAddress(), layout);
init.accept(segment);
checker.accept(segment);
}
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testTooBigForArray() {
try (MemorySegment segment = MemorySegment.ofNativeRestricted(MemoryAddress.NULL, (long)Integer.MAX_VALUE + 10L, null, null, null)) {
segment.toByteArray();
@Test(dataProvider = "elemLayouts",
expectedExceptions = UnsupportedOperationException.class)
public void testTooBigForArray(MemoryLayout layout, Function<MemorySegment, Object> arrayFactory) {
MemoryLayout seq = MemoryLayout.ofSequence((Integer.MAX_VALUE * layout.byteSize()) + 1, layout);
//do not really allocate here, as it's way too much memory
try (MemorySegment segment = MemoryAddress.NULL.asSegmentRestricted(seq.byteSize())) {
arrayFactory.apply(segment);
}
}
@Test(expectedExceptions = IllegalStateException.class)
public void testArrayFromClosedSegment() {
MemorySegment segment = MemorySegment.allocateNative(8);
@Test(dataProvider = "elemLayouts",
expectedExceptions = UnsupportedOperationException.class)
public void testBadSize(MemoryLayout layout, Function<MemorySegment, Object> arrayFactory) {
if (layout.byteSize() == 1) throw new UnsupportedOperationException(); //make it fail
try (MemorySegment segment = MemorySegment.allocateNative(layout.byteSize() + 1)) {
arrayFactory.apply(segment);
}
}
@Test(dataProvider = "elemLayouts",
expectedExceptions = IllegalStateException.class)
public void testArrayFromClosedSegment(MemoryLayout layout, Function<MemorySegment, Object> arrayFactory) {
MemorySegment segment = MemorySegment.allocateNative(layout);
segment.close();
segment.toByteArray();
arrayFactory.apply(segment);
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testArrayFromHeapSegmentWithoutAccess() {
MemorySegment segment = MemorySegment.ofArray(new byte[8]);
segment = segment.withAccessModes(segment.accessModes() & ~READ);
segment.toByteArray();
@Test(dataProvider = "elemLayouts",
expectedExceptions = UnsupportedOperationException.class)
public void testArrayFromHeapSegmentWithoutAccess(MemoryLayout layout, Function<MemorySegment, Object> arrayFactory) {
MemorySegment segment = MemorySegment.ofArray(new byte[(int)layout.byteSize()]);
segment = segment.withAccessModes(MemorySegment.ALL_ACCESS & ~READ);
arrayFactory.apply(segment);
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testArrayFromNativeSegmentWithoutAccess() {
MemorySegment segment = MemorySegment.allocateNative(8);
segment = segment.withAccessModes(segment.accessModes() & ~READ);
segment.toByteArray();
@Test(dataProvider = "elemLayouts",
expectedExceptions = UnsupportedOperationException.class)
public void testArrayFromNativeSegmentWithoutAccess(MemoryLayout layout, Function<MemorySegment, Object> arrayFactory) {
try (MemorySegment segment = MemorySegment.allocateNative(layout).withAccessModes(MemorySegment.ALL_ACCESS & ~READ)) {
arrayFactory.apply(segment);
}
}
@DataProvider(name = "arrays")
public Object[][] nativeAccessOps() {
Consumer<MemoryAddress> byteInitializer =
Consumer<MemorySegment> byteInitializer =
(base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
Consumer<MemoryAddress> charInitializer =
Consumer<MemorySegment> charInitializer =
(base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
Consumer<MemoryAddress> shortInitializer =
Consumer<MemorySegment> shortInitializer =
(base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
Consumer<MemoryAddress> intInitializer =
Consumer<MemorySegment> intInitializer =
(base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
Consumer<MemoryAddress> floatInitializer =
Consumer<MemorySegment> floatInitializer =
(base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
Consumer<MemoryAddress> longInitializer =
Consumer<MemorySegment> longInitializer =
(base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
Consumer<MemoryAddress> doubleInitializer =
Consumer<MemorySegment> doubleInitializer =
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
Consumer<MemorySegment> byteChecker =
(base) -> checkBytes(base, bytes, MemorySegment::toByteArray, (addr, pos) -> (byte)byteHandle.get(addr, pos));
Consumer<MemorySegment> shortChecker =
(base) -> checkBytes(base, shorts, MemorySegment::toShortArray, (addr, pos) -> (short)shortHandle.get(addr, pos));
Consumer<MemorySegment> charChecker =
(base) -> checkBytes(base, chars, MemorySegment::toCharArray, (addr, pos) -> (char)charHandle.get(addr, pos));
Consumer<MemorySegment> intChecker =
(base) -> checkBytes(base, ints, MemorySegment::toIntArray, (addr, pos) -> (int)intHandle.get(addr, pos));
Consumer<MemorySegment> floatChecker =
(base) -> checkBytes(base, floats, MemorySegment::toFloatArray, (addr, pos) -> (float)floatHandle.get(addr, pos));
Consumer<MemorySegment> longChecker =
(base) -> checkBytes(base, longs, MemorySegment::toLongArray, (addr, pos) -> (long)longHandle.get(addr, pos));
Consumer<MemorySegment> doubleChecker =
(base) -> checkBytes(base, doubles, MemorySegment::toDoubleArray, (addr, pos) -> (double)doubleHandle.get(addr, pos));
return new Object[][]{
{byteInitializer, bytes},
{charInitializer, chars},
{shortInitializer, shorts},
{intInitializer, ints},
{floatInitializer, floats},
{longInitializer, longs},
{doubleInitializer, doubles}
{byteInitializer, byteChecker, bytes},
{charInitializer, charChecker, chars},
{shortInitializer, shortChecker, shorts},
{intInitializer, intChecker, ints},
{floatInitializer, floatChecker, floats},
{longInitializer, longChecker, longs},
{doubleInitializer, doubleChecker, doubles}
};
}
@DataProvider(name = "elemLayouts")
public Object[][] elemLayouts() {
return new Object[][] {
{ MemoryLayouts.JAVA_BYTE, (Function<MemorySegment, Object>) MemorySegment::toByteArray },
{ MemoryLayouts.JAVA_SHORT, (Function<MemorySegment, Object>) MemorySegment::toShortArray },
{ MemoryLayouts.JAVA_CHAR, (Function<MemorySegment, Object>) MemorySegment::toCharArray },
{ MemoryLayouts.JAVA_INT, (Function<MemorySegment, Object>) MemorySegment::toIntArray },
{ MemoryLayouts.JAVA_FLOAT, (Function<MemorySegment, Object>) MemorySegment::toFloatArray },
{ MemoryLayouts.JAVA_LONG, (Function<MemorySegment, Object>) MemorySegment::toLongArray },
{ MemoryLayouts.JAVA_DOUBLE, (Function<MemorySegment, Object>) MemorySegment::toDoubleArray }
};
}
}

View File

@ -4,9 +4,7 @@
*
* 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.
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
@ -31,7 +29,8 @@
*/
import jdk.incubator.foreign.MappedMemorySegment;
import jdk.incubator.foreign.MappedMemorySegments;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryAddress;
@ -62,7 +61,9 @@ import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@ -73,7 +74,6 @@ import java.util.stream.Stream;
import jdk.internal.foreign.HeapMemorySegmentImpl;
import jdk.internal.foreign.MappedMemorySegmentImpl;
import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;
import org.testng.SkipException;
import org.testng.annotations.*;
@ -134,23 +134,14 @@ public class TestByteBuffer {
static VarHandle indexHandle = tuples.varHandle(int.class, PathElement.sequenceElement(), PathElement.groupElement("index"));
static VarHandle valueHandle = tuples.varHandle(float.class, PathElement.sequenceElement(), PathElement.groupElement("value"));
static VarHandle byteHandle = bytes.varHandle(byte.class, PathElement.sequenceElement());
static VarHandle charHandle = chars.varHandle(char.class, PathElement.sequenceElement());
static VarHandle shortHandle = shorts.varHandle(short.class, PathElement.sequenceElement());
static VarHandle intHandle = ints.varHandle(int.class, PathElement.sequenceElement());
static VarHandle floatHandle = floats.varHandle(float.class, PathElement.sequenceElement());
static VarHandle longHandle = longs.varHandle(long.class, PathElement.sequenceElement());
static VarHandle doubleHandle = doubles.varHandle(double.class, PathElement.sequenceElement());
static void initTuples(MemoryAddress base, long count) {
static void initTuples(MemorySegment base, long count) {
for (long i = 0; i < count ; i++) {
indexHandle.set(base, i, (int)i);
valueHandle.set(base, i, (float)(i / 500f));
}
}
static void checkTuples(MemoryAddress base, ByteBuffer bb, long count) {
static void checkTuples(MemorySegment base, ByteBuffer bb, long count) {
for (long i = 0; i < count ; i++) {
int index;
float value;
@ -160,25 +151,25 @@ public class TestByteBuffer {
}
}
static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer<MemorySegment, Long> handleSetter) {
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
handleSetter.accept(base, i);
}
}
static <Z extends Buffer> void checkBytes(MemoryAddress base, SequenceLayout layout,
static <Z extends Buffer> void checkBytes(MemorySegment base, SequenceLayout layout,
Function<ByteBuffer, Z> bufFactory,
BiFunction<MemoryAddress, Long, Object> handleExtractor,
BiFunction<MemorySegment, Long, Object> handleExtractor,
Function<Z, Object> bufferExtractor) {
long nelems = layout.elementCount().getAsLong();
long elemSize = layout.elementLayout().byteSize();
for (long i = 0 ; i < nelems ; i++) {
long limit = nelems - i;
MemorySegment resizedSegment = base.segment().asSlice(i * elemSize, limit * elemSize);
MemorySegment resizedSegment = base.asSlice(i * elemSize, limit * elemSize);
ByteBuffer bb = resizedSegment.asByteBuffer();
Z z = bufFactory.apply(bb);
for (long j = i ; j < limit ; j++) {
Object handleValue = handleExtractor.apply(resizedSegment.baseAddress(), j - i);
Object handleValue = handleExtractor.apply(resizedSegment, j - i);
Object bufferValue = bufferExtractor.apply(z);
if (handleValue instanceof Number) {
assertEquals(((Number)handleValue).longValue(), j);
@ -194,11 +185,10 @@ public class TestByteBuffer {
@Test
public void testOffheap() {
try (MemorySegment segment = MemorySegment.allocateNative(tuples)) {
MemoryAddress base = segment.baseAddress();
initTuples(base, tuples.elementCount().getAsLong());
initTuples(segment, tuples.elementCount().getAsLong());
ByteBuffer bb = segment.asByteBuffer();
checkTuples(base, bb, tuples.elementCount().getAsLong());
checkTuples(segment, bb, tuples.elementCount().getAsLong());
}
}
@ -206,11 +196,10 @@ public class TestByteBuffer {
public void testHeap() {
byte[] arr = new byte[(int) tuples.byteSize()];
MemorySegment region = MemorySegment.ofArray(arr);
MemoryAddress base = region.baseAddress();
initTuples(base, tuples.elementCount().getAsLong());
initTuples(region, tuples.elementCount().getAsLong());
ByteBuffer bb = region.asByteBuffer();
checkTuples(base, bb, tuples.elementCount().getAsLong());
checkTuples(region, bb, tuples.elementCount().getAsLong());
}
@Test
@ -223,8 +212,7 @@ public class TestByteBuffer {
try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE)) {
withMappedBuffer(channel, FileChannel.MapMode.READ_WRITE, 0, tuples.byteSize(), mbb -> {
MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
MemoryAddress base = segment.baseAddress();
initTuples(base, tuples.elementCount().getAsLong());
initTuples(segment, tuples.elementCount().getAsLong());
mbb.force();
});
}
@ -233,20 +221,19 @@ public class TestByteBuffer {
try (FileChannel channel = FileChannel.open(f.toPath(), StandardOpenOption.READ)) {
withMappedBuffer(channel, FileChannel.MapMode.READ_ONLY, 0, tuples.byteSize(), mbb -> {
MemorySegment segment = MemorySegment.ofByteBuffer(mbb);
MemoryAddress base = segment.baseAddress();
checkTuples(base, mbb, tuples.elementCount().getAsLong());
checkTuples(segment, mbb, tuples.elementCount().getAsLong());
});
}
}
@Test
public void testDefaultAccessModesMappedSegment() throws Throwable {
try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 0L, 8, FileChannel.MapMode.READ_WRITE)) {
try (MemorySegment segment = MemorySegment.mapFile(tempPath, 0L, 8, FileChannel.MapMode.READ_WRITE)) {
assertTrue(segment.hasAccessModes(ALL_ACCESS));
assertEquals(segment.accessModes(), ALL_ACCESS);
}
try (MappedMemorySegment segment = MemorySegment.mapFromPath(tempPath, 0L, 8, FileChannel.MapMode.READ_ONLY)) {
try (MemorySegment segment = MemorySegment.mapFile(tempPath, 0L, 8, FileChannel.MapMode.READ_ONLY)) {
assertTrue(segment.hasAccessModes(ALL_ACCESS & ~WRITE));
assertEquals(segment.accessModes(), ALL_ACCESS & ~WRITE);
}
@ -259,19 +246,29 @@ public class TestByteBuffer {
f.deleteOnExit();
//write to channel
try (MappedMemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
MemoryAddress base = segment.baseAddress();
initTuples(base, tuples.elementCount().getAsLong());
segment.force();
try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
initTuples(segment, tuples.elementCount().getAsLong());
MappedMemorySegments.force(segment);
}
//read from channel
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
MemoryAddress base = segment.baseAddress();
checkTuples(base, segment.asByteBuffer(), tuples.elementCount().getAsLong());
try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
checkTuples(segment, segment.asByteBuffer(), tuples.elementCount().getAsLong());
}
}
@Test(dataProvider = "mappedOps", expectedExceptions = IllegalStateException.class)
public void testMappedSegmentOperations(MappedSegmentOp mappedBufferOp) throws Throwable {
File f = new File("test3.out");
f.createNewFile();
f.deleteOnExit();
MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 8, FileChannel.MapMode.READ_WRITE);
assertTrue(segment.isMapped());
segment.close();
mappedBufferOp.apply(segment);
}
@Test
public void testMappedSegmentOffset() throws Throwable {
File f = new File("test3.out");
@ -283,19 +280,17 @@ public class TestByteBuffer {
// write one at a time
for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {
//write to channel
try (MappedMemorySegment segment = MemorySegment.mapFromPath(f.toPath(), i, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
MemoryAddress base = segment.baseAddress();
initTuples(base, 1);
segment.force();
try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), i, tuples.byteSize(), FileChannel.MapMode.READ_WRITE)) {
initTuples(segment, 1);
MappedMemorySegments.force(segment);
}
}
// check one at a time
for (int i = 0 ; i < tuples.byteSize() ; i += tupleLayout.byteSize()) {
//read from channel
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
MemoryAddress base = segment.baseAddress();
checkTuples(base, segment.asByteBuffer(), 1);
try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, tuples.byteSize(), FileChannel.MapMode.READ_ONLY)) {
checkTuples(segment, segment.asByteBuffer(), 1);
}
}
}
@ -320,35 +315,27 @@ public class TestByteBuffer {
}
@Test(dataProvider = "bufferOps")
public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
public void testScopedBuffer(Function<ByteBuffer, Buffer> bufferFactory, @NoInjection Method method, Object[] args) {
Buffer bb;
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
MemoryAddress base = segment.baseAddress();
bb = bufferFactory.apply(segment.asByteBuffer());
}
//outside of scope!!
for (Map.Entry<Method, Object[]> e : members.entrySet()) {
if (!e.getKey().getName().contains("get") &&
!e.getKey().getName().contains("put")) {
//skip
return;
}
try {
e.getKey().invoke(bb, e.getValue());
assertTrue(false);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof IllegalStateException) {
//all get/set buffer operation should fail because of the scope check
assertTrue(ex.getCause().getMessage().contains("already closed"));
} else {
//all other exceptions were unexpected - fail
assertTrue(false);
}
} catch (Throwable ex) {
//unexpected exception - fail
assertTrue(false);
try {
method.invoke(bb, args);
fail("Exception expected");
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof IllegalStateException) {
//all get/set buffer operation should fail because of the scope check
assertTrue(ex.getCause().getMessage().contains("already closed"));
} else {
//all other exceptions were unexpected - fail
fail("Unexpected exception", cause);
}
} catch (Throwable ex) {
//unexpected exception - fail
fail("Unexpected exception", ex);
}
}
@ -387,63 +374,59 @@ public class TestByteBuffer {
}
@Test(dataProvider = "bufferOps")
public void testDirectBuffer(Function<ByteBuffer, Buffer> bufferFactory, Map<Method, Object[]> members) {
public void testDirectBuffer(Function<ByteBuffer, Buffer> bufferFactory, @NoInjection Method method, Object[] args) {
try (MemorySegment segment = MemorySegment.allocateNative(bytes)) {
MemoryAddress base = segment.baseAddress();
Buffer bb = bufferFactory.apply(segment.asByteBuffer());
assertTrue(bb.isDirect());
DirectBuffer directBuffer = ((DirectBuffer)bb);
assertEquals(directBuffer.address(), ((MemoryAddressImpl)base).unsafeGetOffset());
assertEquals(directBuffer.address(), segment.address().toRawLongValue());
assertTrue((directBuffer.attachment() == null) == (bb instanceof ByteBuffer));
assertTrue(directBuffer.cleaner() == null);
}
}
@Test(dataProvider="resizeOps")
public void testResizeOffheap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
public void testResizeOffheap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
MemoryAddress base = segment.baseAddress();
initializer.accept(base);
checker.accept(base);
initializer.accept(segment);
checker.accept(segment);
}
}
@Test(dataProvider="resizeOps")
public void testResizeHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
public void testResizeHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
checkByteArrayAlignment(seq.elementLayout());
int capacity = (int)seq.byteSize();
MemoryAddress base = MemorySegment.ofArray(new byte[capacity]).baseAddress();
MemorySegment base = MemorySegment.ofArray(new byte[capacity]);
initializer.accept(base);
checker.accept(base);
}
@Test(dataProvider="resizeOps")
public void testResizeBuffer(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
public void testResizeBuffer(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
checkByteArrayAlignment(seq.elementLayout());
int capacity = (int)seq.byteSize();
MemoryAddress base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity])).baseAddress();
MemorySegment base = MemorySegment.ofByteBuffer(ByteBuffer.wrap(new byte[capacity]));
initializer.accept(base);
checker.accept(base);
}
@Test(dataProvider="resizeOps")
public void testResizeRoundtripHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
public void testResizeRoundtripHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
checkByteArrayAlignment(seq.elementLayout());
int capacity = (int)seq.byteSize();
byte[] arr = new byte[capacity];
MemorySegment segment = MemorySegment.ofArray(arr);
MemoryAddress first = segment.baseAddress();
initializer.accept(first);
MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
initializer.accept(segment);
MemorySegment second = MemorySegment.ofByteBuffer(segment.asByteBuffer());
checker.accept(second);
}
@Test(dataProvider="resizeOps")
public void testResizeRoundtripNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
public void testResizeRoundtripNative(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
MemoryAddress first = segment.baseAddress();
initializer.accept(first);
MemoryAddress second = MemorySegment.ofByteBuffer(segment.asByteBuffer()).baseAddress();
initializer.accept(segment);
MemorySegment second = MemorySegment.ofByteBuffer(segment.asByteBuffer());
checker.accept(second);
}
}
@ -460,7 +443,7 @@ public class TestByteBuffer {
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testTooBigForByteBuffer() {
try (MemorySegment segment = MemorySegment.ofNativeRestricted(MemoryAddress.NULL, (long)Integer.MAX_VALUE + 10L, null, null, null)) {
try (MemorySegment segment = MemoryAddress.NULL.asSegmentRestricted(Integer.MAX_VALUE + 10L)) {
segment.asByteBuffer();
}
}
@ -470,7 +453,7 @@ public class TestByteBuffer {
File f = new File("testNeg1.out");
f.createNewFile();
f.deleteOnExit();
MemorySegment.mapFromPath(f.toPath(), 0L, -1, FileChannel.MapMode.READ_WRITE);
MemorySegment.mapFile(f.toPath(), 0L, -1, FileChannel.MapMode.READ_WRITE);
}
@Test(expectedExceptions = IllegalArgumentException.class)
@ -478,39 +461,39 @@ public class TestByteBuffer {
File f = new File("testNeg2.out");
f.createNewFile();
f.deleteOnExit();
MemorySegment.mapFromPath(f.toPath(), -1, 1, FileChannel.MapMode.READ_WRITE);
MemorySegment.mapFile(f.toPath(), -1, 1, FileChannel.MapMode.READ_WRITE);
}
public void testMapZeroSize() throws IOException {
File f = new File("testPos1.out");
f.createNewFile();
f.deleteOnExit();
try (MemorySegment segment = MemorySegment.mapFromPath(f.toPath(), 0L, 0L, FileChannel.MapMode.READ_WRITE)) {
try (MemorySegment segment = MemorySegment.mapFile(f.toPath(), 0L, 0L, FileChannel.MapMode.READ_WRITE)) {
assertEquals(segment.byteSize(), 0);
}
}
@Test(dataProvider="resizeOps")
public void testCopyHeapToNative(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
public void testCopyHeapToNative(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
checkByteArrayAlignment(seq.elementLayout());
int bytes = (int)seq.byteSize();
try (MemorySegment nativeArray = MemorySegment.allocateNative(bytes);
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
initializer.accept(heapArray.baseAddress());
initializer.accept(heapArray);
nativeArray.copyFrom(heapArray);
checker.accept(nativeArray.baseAddress());
checker.accept(nativeArray);
}
}
@Test(dataProvider="resizeOps")
public void testCopyNativeToHeap(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
public void testCopyNativeToHeap(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
checkByteArrayAlignment(seq.elementLayout());
int bytes = (int)seq.byteSize();
try (MemorySegment nativeArray = MemorySegment.allocateNative(seq);
MemorySegment heapArray = MemorySegment.ofArray(new byte[bytes])) {
initializer.accept(nativeArray.baseAddress());
initializer.accept(nativeArray);
heapArray.copyFrom(nativeArray);
checker.accept(heapArray.baseAddress());
checker.accept(heapArray);
}
}
@ -546,6 +529,16 @@ public class TestByteBuffer {
assertEquals(bb.capacity(), segment.byteSize());
}
@Test(dataProvider="bufferSources")
public void bufferProperties(ByteBuffer bb, Predicate<MemorySegment> _unused) {
try (MemorySegment segment = MemorySegment.ofByteBuffer(bb)) {
ByteBuffer buffer = segment.asByteBuffer();
assertEquals(buffer.position(), 0);
assertEquals(buffer.capacity(), segment.byteSize());
assertEquals(buffer.limit(), segment.byteSize());
}
}
@Test
public void testRoundTripAccess() {
try(MemorySegment ms = MemorySegment.allocateNative(4)) {
@ -562,34 +555,77 @@ public class TestByteBuffer {
s1.close(); // memory freed
intHandle.set(s2.baseAddress(), 0L, 10); // Dead access!
MemoryAccess.setInt(s2, 10); // Dead access!
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testIOOnSharedSegmentBuffer() throws IOException {
File tmp = File.createTempFile("tmp", "txt");
tmp.deleteOnExit();
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.WRITE)) {
MemorySegment segment = MemorySegment.allocateNative(10).share();
for (int i = 0; i < 10; i++) {
MemoryAccess.setByteAtOffset(segment, i, (byte) i);
}
ByteBuffer bb = segment.asByteBuffer();
segment.close();
channel.write(bb);
}
}
@Test(expectedExceptions = IllegalStateException.class)
public void testIOOnClosedConfinedSegmentBuffer() throws IOException {
File tmp = File.createTempFile("tmp", "txt");
tmp.deleteOnExit();
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.WRITE)) {
MemorySegment segment = MemorySegment.allocateNative(10);
for (int i = 0; i < 10; i++) {
MemoryAccess.setByteAtOffset(segment, i, (byte) i);
}
ByteBuffer bb = segment.asByteBuffer();
segment.close();
channel.write(bb);
}
}
public void testIOOnClosedConfinedSegment() throws IOException {
File tmp = File.createTempFile("tmp", "txt");
tmp.deleteOnExit();
try (FileChannel channel = FileChannel.open(tmp.toPath(), StandardOpenOption.WRITE)) {
MemorySegment segment = MemorySegment.allocateNative(10);
for (int i = 0; i < 10; i++) {
MemoryAccess.setByteAtOffset(segment, i, (byte) i);
}
ByteBuffer bb = segment.asByteBuffer();
channel.write(bb);
}
}
@DataProvider(name = "bufferOps")
public static Object[][] bufferOps() throws Throwable {
return new Object[][]{
{ (Function<ByteBuffer, Buffer>) bb -> bb, bufferMembers(ByteBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asCharBuffer, bufferMembers(CharBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asShortBuffer, bufferMembers(ShortBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asIntBuffer, bufferMembers(IntBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asFloatBuffer, bufferMembers(FloatBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asLongBuffer, bufferMembers(LongBuffer.class)},
{ (Function<ByteBuffer, Buffer>) ByteBuffer::asDoubleBuffer, bufferMembers(DoubleBuffer.class)},
};
List<Object[]> args = new ArrayList<>();
bufferOpsArgs(args, bb -> bb, ByteBuffer.class);
bufferOpsArgs(args, ByteBuffer::asCharBuffer, CharBuffer.class);
bufferOpsArgs(args, ByteBuffer::asShortBuffer, ShortBuffer.class);
bufferOpsArgs(args, ByteBuffer::asIntBuffer, IntBuffer.class);
bufferOpsArgs(args, ByteBuffer::asFloatBuffer, FloatBuffer.class);
bufferOpsArgs(args, ByteBuffer::asLongBuffer, LongBuffer.class);
bufferOpsArgs(args, ByteBuffer::asDoubleBuffer, DoubleBuffer.class);
return args.toArray(Object[][]::new);
}
static Map<Method, Object[]> bufferMembers(Class<?> bufferClass) {
Map<Method, Object[]> members = new HashMap<>();
static void bufferOpsArgs(List<Object[]> argsList, Function<ByteBuffer, Buffer> factory, Class<?> bufferClass) {
for (Method m : bufferClass.getMethods()) {
//skip statics and method declared in j.l.Object
if (m.getDeclaringClass().equals(Object.class) ||
(m.getModifiers() & Modifier.STATIC) != 0) continue;
if (m.getDeclaringClass().equals(Object.class)
|| ((m.getModifiers() & Modifier.STATIC) != 0)
|| (!m.getName().contains("get") && !m.getName().contains("put"))
|| m.getParameterCount() > 2) continue;
Object[] args = Stream.of(m.getParameterTypes())
.map(TestByteBuffer::defaultValue)
.toArray();
members.put(m, args);
argsList.add(new Object[] { factory, m, args });
}
return members;
}
@DataProvider(name = "bufferHandleOps")
@ -622,35 +658,35 @@ public class TestByteBuffer {
@DataProvider(name = "resizeOps")
public Object[][] resizeOps() {
Consumer<MemoryAddress> byteInitializer =
(base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
Consumer<MemoryAddress> charInitializer =
(base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
Consumer<MemoryAddress> shortInitializer =
(base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
Consumer<MemoryAddress> intInitializer =
(base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
Consumer<MemoryAddress> floatInitializer =
(base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
Consumer<MemoryAddress> longInitializer =
(base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
Consumer<MemoryAddress> doubleInitializer =
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
Consumer<MemorySegment> byteInitializer =
(base) -> initBytes(base, bytes, (addr, pos) -> MemoryAccess.setByteAtOffset(addr, pos, (byte)(long)pos));
Consumer<MemorySegment> charInitializer =
(base) -> initBytes(base, chars, (addr, pos) -> MemoryAccess.setCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (char)(long)pos));
Consumer<MemorySegment> shortInitializer =
(base) -> initBytes(base, shorts, (addr, pos) -> MemoryAccess.setShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (short)(long)pos));
Consumer<MemorySegment> intInitializer =
(base) -> initBytes(base, ints, (addr, pos) -> MemoryAccess.setIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (int)(long)pos));
Consumer<MemorySegment> floatInitializer =
(base) -> initBytes(base, floats, (addr, pos) -> MemoryAccess.setFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (float)(long)pos));
Consumer<MemorySegment> longInitializer =
(base) -> initBytes(base, longs, (addr, pos) -> MemoryAccess.setLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (long)pos));
Consumer<MemorySegment> doubleInitializer =
(base) -> initBytes(base, doubles, (addr, pos) -> MemoryAccess.setDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN, (double)(long)pos));
Consumer<MemoryAddress> byteChecker =
(base) -> checkBytes(base, bytes, Function.identity(), byteHandle::get, ByteBuffer::get);
Consumer<MemoryAddress> charChecker =
(base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, charHandle::get, CharBuffer::get);
Consumer<MemoryAddress> shortChecker =
(base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, shortHandle::get, ShortBuffer::get);
Consumer<MemoryAddress> intChecker =
(base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, intHandle::get, IntBuffer::get);
Consumer<MemoryAddress> floatChecker =
(base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, floatHandle::get, FloatBuffer::get);
Consumer<MemoryAddress> longChecker =
(base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, longHandle::get, LongBuffer::get);
Consumer<MemoryAddress> doubleChecker =
(base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, doubleHandle::get, DoubleBuffer::get);
Consumer<MemorySegment> byteChecker =
(base) -> checkBytes(base, bytes, Function.identity(), (addr, pos) -> MemoryAccess.getByteAtOffset(addr, pos), ByteBuffer::get);
Consumer<MemorySegment> charChecker =
(base) -> checkBytes(base, chars, ByteBuffer::asCharBuffer, (addr, pos) -> MemoryAccess.getCharAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), CharBuffer::get);
Consumer<MemorySegment> shortChecker =
(base) -> checkBytes(base, shorts, ByteBuffer::asShortBuffer, (addr, pos) -> MemoryAccess.getShortAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), ShortBuffer::get);
Consumer<MemorySegment> intChecker =
(base) -> checkBytes(base, ints, ByteBuffer::asIntBuffer, (addr, pos) -> MemoryAccess.getIntAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), IntBuffer::get);
Consumer<MemorySegment> floatChecker =
(base) -> checkBytes(base, floats, ByteBuffer::asFloatBuffer, (addr, pos) -> MemoryAccess.getFloatAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), FloatBuffer::get);
Consumer<MemorySegment> longChecker =
(base) -> checkBytes(base, longs, ByteBuffer::asLongBuffer, (addr, pos) -> MemoryAccess.getLongAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), LongBuffer::get);
Consumer<MemorySegment> doubleChecker =
(base) -> checkBytes(base, doubles, ByteBuffer::asDoubleBuffer, (addr, pos) -> MemoryAccess.getDoubleAtIndex(addr, pos, ByteOrder.BIG_ENDIAN), DoubleBuffer::get);
return new Object[][]{
{byteChecker, byteInitializer, bytes},
@ -704,6 +740,22 @@ public class TestByteBuffer {
} else {
throw new IllegalStateException();
}
} else if (c == String.class) {
return "asdf";
} else if (c == ByteBuffer.class) {
return ByteBuffer.wrap(new byte[1]);
} else if (c == CharBuffer.class) {
return CharBuffer.wrap(new char[1]);
} else if (c == ShortBuffer.class) {
return ShortBuffer.wrap(new short[1]);
} else if (c == IntBuffer.class) {
return IntBuffer.wrap(new int[1]);
} else if (c == FloatBuffer.class) {
return FloatBuffer.wrap(new float[1]);
} else if (c == LongBuffer.class) {
return LongBuffer.wrap(new long[1]);
} else if (c == DoubleBuffer.class) {
return DoubleBuffer.wrap(new double[1]);
} else {
return null;
}
@ -731,4 +783,32 @@ public class TestByteBuffer {
throw new ExceptionInInitializerError(ex);
}
}
enum MappedSegmentOp {
LOAD(MappedMemorySegments::load),
UNLOAD(MappedMemorySegments::unload),
IS_LOADED(MappedMemorySegments::isLoaded),
FORCE(MappedMemorySegments::force),
BUFFER_LOAD(m -> ((MappedByteBuffer)m.asByteBuffer()).load()),
BUFFER_IS_LOADED(m -> ((MappedByteBuffer)m.asByteBuffer()).isLoaded()),
BUFFER_FORCE(m -> ((MappedByteBuffer)m.asByteBuffer()).force());
private Consumer<MemorySegment> segmentOp;
MappedSegmentOp(Consumer<MemorySegment> segmentOp) {
this.segmentOp = segmentOp;
}
void apply(MemorySegment segment) {
segmentOp.accept(segment);
}
}
@DataProvider(name = "mappedOps")
public static Object[][] mappedOps() {
return Stream.of(MappedSegmentOp.values())
.map(op -> new Object[] { op })
.toArray(Object[][]::new);
}
}

View File

@ -0,0 +1,185 @@
/*
* 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.
*
* 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.
*/
/*
* @test
* @modules java.base/jdk.internal.ref
* jdk.incubator.foreign/jdk.incubator.foreign
* @run testng/othervm -Dforeign.restricted=permit TestCleaner
*/
import jdk.incubator.foreign.MemorySegment;
import java.lang.ref.Cleaner;
import jdk.internal.ref.CleanerFactory;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class TestCleaner {
static class SegmentState {
private AtomicInteger cleanupCalls = new AtomicInteger(0);
void cleanup() {
cleanupCalls.incrementAndGet();
}
int cleanupCalls() {
return cleanupCalls.get();
}
}
@Test(dataProvider = "cleaners")
public void testAtMostOnce(RegisterKind registerKind, Supplier<Cleaner> cleanerFactory, SegmentFunction segmentFunction) {
SegmentState segmentState = new SegmentState();
MemorySegment root = MemorySegment.allocateNative(10).share();
MemorySegment segment = root.address().asSegmentRestricted(10, () -> {
root.close();
segmentState.cleanup();
}, null);
if (registerKind == RegisterKind.BEFORE) {
// register cleaners before
segment = segment.registerCleaner(cleanerFactory.get());
}
kickGCAndCheck(segmentState, segment);
segment = segmentFunction.apply(segment);
kickGCAndCheck(segmentState, segment);
if (segment.isAlive() && registerKind == RegisterKind.AFTER) {
// register cleaners after
segment = segment.registerCleaner(cleanerFactory.get());
}
kickGCAndCheck(segmentState, segment);
segment = null;
while (segmentState.cleanupCalls() == 0) {
byte[] b = new byte[100];
System.gc();
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
throw new AssertionError(ex);
}
}
assertEquals(segmentState.cleanupCalls(), 1);
}
private void kickGCAndCheck(SegmentState segmentState, MemorySegment segment) {
for (int i = 0 ; i < 100 ; i++) {
byte[] b = new byte[100];
System.gc();
Thread.onSpinWait();
}
//check that cleanup has not been called by any cleaner yet!
assertEquals(segmentState.cleanupCalls(), segment.isAlive() ? 0 : 1);
}
@Test(dataProvider = "segmentFunctions")
public void testBadDoubleRegister(Supplier<Cleaner> cleanerFactory, SegmentFunction segmentFunction) {
MemorySegment segment = MemorySegment.allocateNative(10);
segment = segment.registerCleaner(cleanerFactory.get());
segment = segmentFunction.apply(segment);
try {
segment.registerCleaner(cleanerFactory.get()); // error here!
fail();
} catch (IllegalStateException ex) {
if (!segment.isAlive()) {
assertTrue(ex.getMessage().contains("This segment is already closed"));
} else {
assertTrue(ex.getMessage().contains("Already registered with a cleaner"));
}
}
}
enum SegmentFunction implements Function<MemorySegment, MemorySegment> {
IDENTITY(Function.identity()),
CLOSE(s -> { s.close(); return s; }),
SHARE(s -> { return s.share(); });
private final Function<MemorySegment, MemorySegment> segmentFunction;
SegmentFunction(Function<MemorySegment, MemorySegment> segmentFunction) {
this.segmentFunction = segmentFunction;
}
@Override
public MemorySegment apply(MemorySegment segment) {
return segmentFunction.apply(segment);
}
}
@DataProvider
static Object[][] segmentFunctions() {
Supplier<?>[] cleaners = {
(Supplier<Cleaner>)Cleaner::create,
(Supplier<Cleaner>)CleanerFactory::cleaner
};
SegmentFunction[] segmentFunctions = SegmentFunction.values();
Object[][] data = new Object[cleaners.length * segmentFunctions.length][3];
for (int cleaner = 0 ; cleaner < cleaners.length ; cleaner++) {
for (int segmentFunction = 0 ; segmentFunction < segmentFunctions.length ; segmentFunction++) {
data[cleaner + (cleaners.length * segmentFunction)] =
new Object[] { cleaners[cleaner], segmentFunctions[segmentFunction] };
}
}
return data;
}
enum RegisterKind {
BEFORE,
AFTER;
}
@DataProvider
static Object[][] cleaners() {
Supplier<?>[] cleaners = {
(Supplier<Cleaner>)Cleaner::create,
(Supplier<Cleaner>)CleanerFactory::cleaner
};
List<Object[]> data = new ArrayList<>();
for (RegisterKind kind : RegisterKind.values()) {
for (Object cleaner : cleaners) {
for (SegmentFunction segmentFunction : SegmentFunction.values()) {
data.add(new Object[] {kind, cleaner, segmentFunction});
}
}
}
return data.toArray(Object[][]::new);
}
}

View File

@ -0,0 +1,260 @@
/*
* 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.
*
* 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.
*/
/*
* @test
* @modules jdk.incubator.foreign java.base/jdk.internal.vm.annotation java.base/jdk.internal.misc
* @key randomness
* @run testng/othervm TestHandshake
* @run testng/othervm -Xint TestHandshake
* @run testng/othervm -XX:TieredStopAtLevel=1 TestHandshake
* @run testng/othervm -XX:-TieredCompilation TestHandshake
*/
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
public class TestHandshake {
static final int ITERATIONS = 5;
static final int SEGMENT_SIZE = 1_000_000;
static final int MAX_DELAY_MILLIS = 500;
static final int MAX_EXECUTOR_WAIT_SECONDS = 10;
static final int MAX_THREAD_SPIN_WAIT_MILLIS = 200;
static final AtomicLong start = new AtomicLong();
@Test(dataProvider = "accessors")
public void testHandshake(String testName, AccessorFactory accessorFactory) throws InterruptedException {
for (int it = 0 ; it < ITERATIONS ; it++) {
MemorySegment segment = MemorySegment.allocateNative(SEGMENT_SIZE).share();
System.out.println("ITERATION " + it);
ExecutorService accessExecutor = Executors.newCachedThreadPool();
start.set(System.currentTimeMillis());
for (int i = 0; i < Runtime.getRuntime().availableProcessors() ; i++) {
accessExecutor.execute(accessorFactory.make(i, segment));
}
int delay = ThreadLocalRandom.current().nextInt(MAX_DELAY_MILLIS);
System.out.println("Starting handshaker with delay set to " + delay + " millis");
Thread.sleep(delay);
accessExecutor.execute(new Handshaker(segment));
accessExecutor.shutdown();
assertTrue(accessExecutor.awaitTermination(MAX_EXECUTOR_WAIT_SECONDS, TimeUnit.SECONDS));
assertTrue(!segment.isAlive());
}
}
static abstract class AbstractSegmentAccessor implements Runnable {
final MemorySegment segment;
final int id;
AbstractSegmentAccessor(int id, MemorySegment segment) {
this.id = id;
this.segment = segment;
}
@Override
public final void run() {
outer: while (segment.isAlive()) {
try {
doAccess();
} catch (IllegalStateException ex) {
long delay = System.currentTimeMillis() - start.get();
System.out.println("Accessor #" + id + " suspending - delay (ms): " + delay);
backoff();
delay = System.currentTimeMillis() - start.get();
System.out.println("Accessor #" + id + " resuming - delay (ms): " + delay);
continue outer;
}
}
long delay = System.currentTimeMillis() - start.get();
System.out.println("Accessor #" + id + " terminated - delay (ms): " + delay);
}
abstract void doAccess();
private void backoff() {
try {
Thread.sleep(ThreadLocalRandom.current().nextInt(MAX_THREAD_SPIN_WAIT_MILLIS));
} catch (InterruptedException ex) {
throw new AssertionError(ex);
}
}
}
static abstract class AbstractBufferAccessor extends AbstractSegmentAccessor {
final ByteBuffer bb;
AbstractBufferAccessor(int id, MemorySegment segment) {
super(id, segment);
this.bb = segment.asByteBuffer();
}
}
static class SegmentAccessor extends AbstractSegmentAccessor {
SegmentAccessor(int id, MemorySegment segment) {
super(id, segment);
}
@Override
void doAccess() {
int sum = 0;
for (int i = 0; i < segment.byteSize(); i++) {
sum += MemoryAccess.getByteAtOffset(segment, i);
}
}
}
static class SegmentCopyAccessor extends AbstractSegmentAccessor {
MemorySegment first, second;
SegmentCopyAccessor(int id, MemorySegment segment) {
super(id, segment);
long split = segment.byteSize() / 2;
first = segment.asSlice(0, split);
second = segment.asSlice(split);
}
@Override
public void doAccess() {
first.copyFrom(second);
}
}
static class SegmentFillAccessor extends AbstractSegmentAccessor {
SegmentFillAccessor(int id, MemorySegment segment) {
super(id, segment);
}
@Override
public void doAccess() {
segment.fill((byte) ThreadLocalRandom.current().nextInt(10));
}
}
static class SegmentMismatchAccessor extends AbstractSegmentAccessor {
final MemorySegment copy;
SegmentMismatchAccessor(int id, MemorySegment segment) {
super(id, segment);
this.copy = MemorySegment.allocateNative(SEGMENT_SIZE).share();
copy.copyFrom(segment);
MemoryAccess.setByteAtOffset(copy, ThreadLocalRandom.current().nextInt(SEGMENT_SIZE), (byte)42);
}
@Override
public void doAccess() {
segment.mismatch(copy);
}
}
static class BufferAccessor extends AbstractBufferAccessor {
BufferAccessor(int id, MemorySegment segment) {
super(id, segment);
}
@Override
public void doAccess() {
int sum = 0;
for (int i = 0; i < bb.capacity(); i++) {
sum += bb.get(i);
}
}
}
static class BufferHandleAccessor extends AbstractBufferAccessor {
static VarHandle handle = MethodHandles.byteBufferViewVarHandle(short[].class, ByteOrder.nativeOrder());
public BufferHandleAccessor(int id, MemorySegment segment) {
super(id, segment);
}
@Override
public void doAccess() {
int sum = 0;
for (int i = 0; i < bb.capacity() / 2; i++) {
sum += (short) handle.get(bb, i);
}
}
};
static class Handshaker implements Runnable {
final MemorySegment segment;
Handshaker(MemorySegment segment) {
this.segment = segment;
}
@Override
public void run() {
while (true) {
try {
segment.close();
break;
} catch (IllegalStateException ex) {
Thread.onSpinWait();
}
}
long delay = System.currentTimeMillis() - start.get();
System.out.println("Segment closed - delay (ms): " + delay);
}
}
interface AccessorFactory {
AbstractSegmentAccessor make(int id, MemorySegment segment);
}
@DataProvider
static Object[][] accessors() {
return new Object[][] {
{ "SegmentAccessor", (AccessorFactory)SegmentAccessor::new },
{ "SegmentCopyAccessor", (AccessorFactory)SegmentCopyAccessor::new },
{ "SegmentMismatchAccesor", (AccessorFactory)SegmentMismatchAccessor::new },
{ "SegmentFillAccessor", (AccessorFactory)SegmentFillAccessor::new },
{ "BufferAccessor", (AccessorFactory)BufferAccessor::new },
{ "BufferHandleAccessor", (AccessorFactory)BufferHandleAccessor::new }
};
}
}

View File

@ -26,16 +26,13 @@
* @run testng TestLayouts
*/
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.*;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.util.function.LongFunction;
import java.util.stream.Stream;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import org.testng.annotations.*;
import static org.testng.Assert.*;
@ -64,14 +61,14 @@ public class TestLayouts {
MemoryLayout.PathElement.sequenceElement());
try (MemorySegment segment = MemorySegment.allocateNative(
layout.map(l -> ((SequenceLayout)l).withElementCount(4), MemoryLayout.PathElement.groupElement("arr")))) {
size_handle.set(segment.baseAddress(), 4);
size_handle.set(segment, 4);
for (int i = 0 ; i < 4 ; i++) {
array_elem_handle.set(segment.baseAddress(), i, (double)i);
array_elem_handle.set(segment, i, (double)i);
}
//check
assertEquals(4, (int)size_handle.get(segment.baseAddress()));
assertEquals(4, (int)size_handle.get(segment));
for (int i = 0 ; i < 4 ; i++) {
assertEquals((double)i, (double)array_elem_handle.get(segment.baseAddress(), i));
assertEquals((double)i, (double)array_elem_handle.get(segment, i));
}
}
}
@ -90,14 +87,14 @@ public class TestLayouts {
MemoryLayout.PathElement.sequenceElement());
try (MemorySegment segment = MemorySegment.allocateNative(
layout.map(l -> ((SequenceLayout)l).withElementCount(4), MemoryLayout.PathElement.groupElement("arr"), MemoryLayout.PathElement.sequenceElement()))) {
size_handle.set(segment.baseAddress(), 4);
size_handle.set(segment, 4);
for (int i = 0 ; i < 4 ; i++) {
array_elem_handle.set(segment.baseAddress(), i, (double)i);
array_elem_handle.set(segment, i, (double)i);
}
//check
assertEquals(4, (int)size_handle.get(segment.baseAddress()));
assertEquals(4, (int)size_handle.get(segment));
for (int i = 0 ; i < 4 ; i++) {
assertEquals((double)i, (double)array_elem_handle.get(segment.baseAddress(), i));
assertEquals((double)i, (double)array_elem_handle.get(segment, i));
}
}
}
@ -109,13 +106,13 @@ public class TestLayouts {
VarHandle indexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement());
// init segment
for (int i = 0 ; i < 10 ; i++) {
indexHandle.set(segment.baseAddress(), (long)i, i);
indexHandle.set(segment, (long)i, i);
}
//check statically indexed handles
for (int i = 0 ; i < 10 ; i++) {
VarHandle preindexHandle = seq.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(i));
int expected = (int)indexHandle.get(segment.baseAddress(), (long)i);
int found = (int)preindexHandle.get(segment.baseAddress());
int expected = (int)indexHandle.get(segment, (long)i);
int found = (int)preindexHandle.get(segment);
assertEquals(expected, found);
}
}
@ -174,7 +171,7 @@ public class TestLayouts {
MemoryLayouts.JAVA_LONG
);
assertEquals(struct.byteSize(), 1 + 1 + 2 + 4 + 8);
assertEquals(struct.byteAlignment(), 8);
assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment());
}
@Test(dataProvider="basicLayouts")
@ -205,7 +202,7 @@ public class TestLayouts {
MemoryLayouts.JAVA_LONG
);
assertEquals(struct.byteSize(), 8);
assertEquals(struct.byteAlignment(), 8);
assertEquals(struct.byteAlignment(), MemoryLayouts.ADDRESS.byteAlignment());
}
@Test(dataProvider = "layoutKinds")
@ -217,8 +214,10 @@ public class TestLayouts {
public void testAlignmentString(MemoryLayout layout, long bitAlign) {
long[] alignments = { 8, 16, 32, 64, 128 };
for (long a : alignments) {
assertFalse(layout.toString().contains("%"));
assertEquals(layout.withBitAlignment(a).toString().contains("%"), a != bitAlign);
if (layout.bitAlignment() == layout.bitSize()) {
assertFalse(layout.toString().contains("%"));
assertEquals(layout.withBitAlignment(a).toString().contains("%"), a != bitAlign);
}
}
}
@ -309,16 +308,12 @@ public class TestLayouts {
@DataProvider(name = "layoutsAndAlignments")
public Object[][] layoutsAndAlignments() {
Object[][] layoutsAndAlignments = new Object[basicLayouts.length * 5][];
Object[][] layoutsAndAlignments = new Object[basicLayouts.length * 4][];
int i = 0;
//add basic layouts
for (MemoryLayout l : basicLayouts) {
layoutsAndAlignments[i++] = new Object[] { l, l.bitAlignment() };
}
//add basic layouts wrapped in a sequence with unspecified size
for (MemoryLayout l : basicLayouts) {
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofSequence(l), l.bitAlignment() };
}
//add basic layouts wrapped in a sequence with given size
for (MemoryLayout l : basicLayouts) {
layoutsAndAlignments[i++] = new Object[] { MemoryLayout.ofSequence(4, l), l.bitAlignment() };

View File

@ -36,7 +36,7 @@ import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout;
import jdk.incubator.foreign.MemoryAddress;
import java.lang.invoke.VarHandle;
import java.util.function.Function;
@ -82,12 +82,11 @@ public class TestMemoryAccess {
}
private void testAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, MemoryLayout layout, VarHandle handle, Checker checker) {
MemoryAddress outer_address;
MemorySegment outer_segment;
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(layout))) {
boolean isRO = !segment.hasAccessModes(MemorySegment.WRITE);
MemoryAddress addr = segment.baseAddress();
try {
checker.check(handle, addr);
checker.check(handle, segment);
if (isRO) {
throw new AssertionError(); //not ok, memory should be immutable
}
@ -98,15 +97,15 @@ public class TestMemoryAccess {
return;
}
try {
checker.check(handle, addr.addOffset(layout.byteSize()));
checker.check(handle, segment.asSlice(layout.byteSize()));
throw new AssertionError(); //not ok, out of bounds
} catch (IndexOutOfBoundsException ex) {
//ok, should fail (out of bounds)
}
outer_address = addr; //leak!
outer_segment = segment; //leak!
}
try {
checker.check(handle, outer_address);
checker.check(handle, outer_segment);
throw new AssertionError(); //not ok, scope is closed
} catch (IllegalStateException ex) {
//ok, should fail (scope is closed)
@ -114,13 +113,12 @@ public class TestMemoryAccess {
}
private void testArrayAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, SequenceLayout seq, VarHandle handle, ArrayChecker checker) {
MemoryAddress outer_address;
MemorySegment outer_segment;
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) {
boolean isRO = !segment.hasAccessModes(MemorySegment.WRITE);
MemoryAddress addr = segment.baseAddress();
try {
for (int i = 0; i < seq.elementCount().getAsLong(); i++) {
checker.check(handle, addr, i);
checker.check(handle, segment, i);
}
if (isRO) {
throw new AssertionError(); //not ok, memory should be immutable
@ -132,15 +130,15 @@ public class TestMemoryAccess {
return;
}
try {
checker.check(handle, addr, seq.elementCount().getAsLong());
checker.check(handle, segment, seq.elementCount().getAsLong());
throw new AssertionError(); //not ok, out of bounds
} catch (IndexOutOfBoundsException ex) {
//ok, should fail (out of bounds)
}
outer_address = addr; //leak!
outer_segment = segment; //leak!
}
try {
checker.check(handle, outer_address, 0);
checker.check(handle, outer_segment, 0);
throw new AssertionError(); //not ok, scope is closed
} catch (IllegalStateException ex) {
//ok, should fail (scope is closed)
@ -183,14 +181,13 @@ public class TestMemoryAccess {
}
private void testMatrixAccessInternal(Function<MemorySegment, MemorySegment> viewFactory, SequenceLayout seq, VarHandle handle, MatrixChecker checker) {
MemoryAddress outer_address;
MemorySegment outer_segment;
try (MemorySegment segment = viewFactory.apply(MemorySegment.allocateNative(seq))) {
boolean isRO = !segment.hasAccessModes(MemorySegment.WRITE);
MemoryAddress addr = segment.baseAddress();
try {
for (int i = 0; i < seq.elementCount().getAsLong(); i++) {
for (int j = 0; j < ((SequenceLayout) seq.elementLayout()).elementCount().getAsLong(); j++) {
checker.check(handle, addr, i, j);
checker.check(handle, segment, i, j);
}
}
if (isRO) {
@ -203,16 +200,16 @@ public class TestMemoryAccess {
return;
}
try {
checker.check(handle, addr, seq.elementCount().getAsLong(),
checker.check(handle, segment, seq.elementCount().getAsLong(),
((SequenceLayout)seq.elementLayout()).elementCount().getAsLong());
throw new AssertionError(); //not ok, out of bounds
} catch (IndexOutOfBoundsException ex) {
//ok, should fail (out of bounds)
}
outer_address = addr; //leak!
outer_segment = segment; //leak!
}
try {
checker.check(handle, outer_address, 0, 0);
checker.check(handle, outer_segment, 0, 0);
throw new AssertionError(); //not ok, scope is closed
} catch (IllegalStateException ex) {
//ok, should fail (scope is closed)
@ -261,41 +258,41 @@ public class TestMemoryAccess {
}
interface Checker {
void check(VarHandle handle, MemoryAddress addr);
void check(VarHandle handle, MemorySegment segment);
Checker BYTE = (handle, addr) -> {
handle.set(addr, (byte)42);
assertEquals(42, (byte)handle.get(addr));
Checker BYTE = (handle, segment) -> {
handle.set(segment, (byte)42);
assertEquals(42, (byte)handle.get(segment));
};
Checker SHORT = (handle, addr) -> {
handle.set(addr, (short)42);
assertEquals(42, (short)handle.get(addr));
Checker SHORT = (handle, segment) -> {
handle.set(segment, (short)42);
assertEquals(42, (short)handle.get(segment));
};
Checker CHAR = (handle, addr) -> {
handle.set(addr, (char)42);
assertEquals(42, (char)handle.get(addr));
Checker CHAR = (handle, segment) -> {
handle.set(segment, (char)42);
assertEquals(42, (char)handle.get(segment));
};
Checker INT = (handle, addr) -> {
handle.set(addr, 42);
assertEquals(42, (int)handle.get(addr));
Checker INT = (handle, segment) -> {
handle.set(segment, 42);
assertEquals(42, (int)handle.get(segment));
};
Checker LONG = (handle, addr) -> {
handle.set(addr, (long)42);
assertEquals(42, (long)handle.get(addr));
Checker LONG = (handle, segment) -> {
handle.set(segment, (long)42);
assertEquals(42, (long)handle.get(segment));
};
Checker FLOAT = (handle, addr) -> {
handle.set(addr, (float)42);
assertEquals((float)42, (float)handle.get(addr));
Checker FLOAT = (handle, segment) -> {
handle.set(segment, (float)42);
assertEquals((float)42, (float)handle.get(segment));
};
Checker DOUBLE = (handle, addr) -> {
handle.set(addr, (double)42);
assertEquals((double)42, (double)handle.get(addr));
Checker DOUBLE = (handle, segment) -> {
handle.set(segment, (double)42);
assertEquals((double)42, (double)handle.get(segment));
};
}
@ -338,41 +335,41 @@ public class TestMemoryAccess {
}
interface ArrayChecker {
void check(VarHandle handle, MemoryAddress addr, long index);
void check(VarHandle handle, MemorySegment segment, long index);
ArrayChecker BYTE = (handle, addr, i) -> {
handle.set(addr, i, (byte)i);
assertEquals(i, (byte)handle.get(addr, i));
ArrayChecker BYTE = (handle, segment, i) -> {
handle.set(segment, i, (byte)i);
assertEquals(i, (byte)handle.get(segment, i));
};
ArrayChecker SHORT = (handle, addr, i) -> {
handle.set(addr, i, (short)i);
assertEquals(i, (short)handle.get(addr, i));
ArrayChecker SHORT = (handle, segment, i) -> {
handle.set(segment, i, (short)i);
assertEquals(i, (short)handle.get(segment, i));
};
ArrayChecker CHAR = (handle, addr, i) -> {
handle.set(addr, i, (char)i);
assertEquals(i, (char)handle.get(addr, i));
ArrayChecker CHAR = (handle, segment, i) -> {
handle.set(segment, i, (char)i);
assertEquals(i, (char)handle.get(segment, i));
};
ArrayChecker INT = (handle, addr, i) -> {
handle.set(addr, i, (int)i);
assertEquals(i, (int)handle.get(addr, i));
ArrayChecker INT = (handle, segment, i) -> {
handle.set(segment, i, (int)i);
assertEquals(i, (int)handle.get(segment, i));
};
ArrayChecker LONG = (handle, addr, i) -> {
handle.set(addr, i, (long)i);
assertEquals(i, (long)handle.get(addr, i));
ArrayChecker LONG = (handle, segment, i) -> {
handle.set(segment, i, (long)i);
assertEquals(i, (long)handle.get(segment, i));
};
ArrayChecker FLOAT = (handle, addr, i) -> {
handle.set(addr, i, (float)i);
assertEquals((float)i, (float)handle.get(addr, i));
ArrayChecker FLOAT = (handle, segment, i) -> {
handle.set(segment, i, (float)i);
assertEquals((float)i, (float)handle.get(segment, i));
};
ArrayChecker DOUBLE = (handle, addr, i) -> {
handle.set(addr, i, (double)i);
assertEquals((double)i, (double)handle.get(addr, i));
ArrayChecker DOUBLE = (handle, segment, i) -> {
handle.set(segment, i, (double)i);
assertEquals((double)i, (double)handle.get(segment, i));
};
}
@ -415,41 +412,41 @@ public class TestMemoryAccess {
}
interface MatrixChecker {
void check(VarHandle handle, MemoryAddress addr, long row, long col);
void check(VarHandle handle, MemorySegment segment, long row, long col);
MatrixChecker BYTE = (handle, addr, r, c) -> {
handle.set(addr, r, c, (byte)(r + c));
assertEquals(r + c, (byte)handle.get(addr, r, c));
MatrixChecker BYTE = (handle, segment, r, c) -> {
handle.set(segment, r, c, (byte)(r + c));
assertEquals(r + c, (byte)handle.get(segment, r, c));
};
MatrixChecker SHORT = (handle, addr, r, c) -> {
handle.set(addr, r, c, (short)(r + c));
assertEquals(r + c, (short)handle.get(addr, r, c));
MatrixChecker SHORT = (handle, segment, r, c) -> {
handle.set(segment, r, c, (short)(r + c));
assertEquals(r + c, (short)handle.get(segment, r, c));
};
MatrixChecker CHAR = (handle, addr, r, c) -> {
handle.set(addr, r, c, (char)(r + c));
assertEquals(r + c, (char)handle.get(addr, r, c));
MatrixChecker CHAR = (handle, segment, r, c) -> {
handle.set(segment, r, c, (char)(r + c));
assertEquals(r + c, (char)handle.get(segment, r, c));
};
MatrixChecker INT = (handle, addr, r, c) -> {
handle.set(addr, r, c, (int)(r + c));
assertEquals(r + c, (int)handle.get(addr, r, c));
MatrixChecker INT = (handle, segment, r, c) -> {
handle.set(segment, r, c, (int)(r + c));
assertEquals(r + c, (int)handle.get(segment, r, c));
};
MatrixChecker LONG = (handle, addr, r, c) -> {
handle.set(addr, r, c, r + c);
assertEquals(r + c, (long)handle.get(addr, r, c));
MatrixChecker LONG = (handle, segment, r, c) -> {
handle.set(segment, r, c, r + c);
assertEquals(r + c, (long)handle.get(segment, r, c));
};
MatrixChecker FLOAT = (handle, addr, r, c) -> {
handle.set(addr, r, c, (float)(r + c));
assertEquals((float)(r + c), (float)handle.get(addr, r, c));
MatrixChecker FLOAT = (handle, segment, r, c) -> {
handle.set(segment, r, c, (float)(r + c));
assertEquals((float)(r + c), (float)handle.get(segment, r, c));
};
MatrixChecker DOUBLE = (handle, addr, r, c) -> {
handle.set(addr, r, c, (double)(r + c));
assertEquals((double)(r + c), (double)handle.get(addr, r, c));
MatrixChecker DOUBLE = (handle, segment, r, c) -> {
handle.set(segment, r, c, (double)(r + c));
assertEquals((double)(r + c), (double)handle.get(segment, r, c));
};
}

View File

@ -31,7 +31,6 @@ import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.GroupLayout;
import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import jdk.incubator.foreign.ValueLayout;
@ -51,9 +50,8 @@ public class TestMemoryAlignment {
assertEquals(aligned.bitAlignment(), align); //unreasonable alignment here, to make sure access throws
VarHandle vh = aligned.varHandle(int.class);
try (MemorySegment segment = MemorySegment.allocateNative(aligned)) {
MemoryAddress addr = segment.baseAddress();
vh.set(addr, -42);
int val = (int)vh.get(addr);
vh.set(segment, -42);
int val = (int)vh.get(segment);
assertEquals(val, -42);
}
}
@ -67,8 +65,7 @@ public class TestMemoryAlignment {
assertEquals(alignedGroup.bitAlignment(), align);
VarHandle vh = aligned.varHandle(int.class);
try (MemorySegment segment = MemorySegment.allocateNative(alignedGroup)) {
MemoryAddress addr = segment.baseAddress();
vh.set(addr.addOffset(1L), -42);
vh.set(segment.asSlice(1L), -42);
assertEquals(align, 8); //this is the only case where access is aligned
} catch (IllegalStateException ex) {
assertNotEquals(align, 8); //if align != 8, access is always unaligned
@ -94,9 +91,8 @@ public class TestMemoryAlignment {
try {
VarHandle vh = layout.varHandle(int.class, PathElement.sequenceElement());
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
MemoryAddress addr = segment.baseAddress();
for (long i = 0 ; i < 5 ; i++) {
vh.set(addr, i, -42);
vh.set(segment, i, -42);
}
}
} catch (UnsupportedOperationException ex) {
@ -118,13 +114,12 @@ public class TestMemoryAlignment {
VarHandle vh_s = g.varHandle(short.class, PathElement.groupElement("b"));
VarHandle vh_i = g.varHandle(int.class, PathElement.groupElement("c"));
try (MemorySegment segment = MemorySegment.allocateNative(g)) {
MemoryAddress addr = segment.baseAddress();
vh_c.set(addr, Byte.MIN_VALUE);
assertEquals(vh_c.get(addr), Byte.MIN_VALUE);
vh_s.set(addr, Short.MIN_VALUE);
assertEquals(vh_s.get(addr), Short.MIN_VALUE);
vh_i.set(addr, Integer.MIN_VALUE);
assertEquals(vh_i.get(addr), Integer.MIN_VALUE);
vh_c.set(segment, Byte.MIN_VALUE);
assertEquals(vh_c.get(segment), Byte.MIN_VALUE);
vh_s.set(segment, Short.MIN_VALUE);
assertEquals(vh_s.get(segment), Short.MIN_VALUE);
vh_i.set(segment, Integer.MIN_VALUE);
assertEquals(vh_i.get(segment), Integer.MIN_VALUE);
}
}

View File

@ -27,7 +27,6 @@
* @run testng TestMemoryCopy
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import org.testng.annotations.DataProvider;
@ -46,21 +45,19 @@ public class TestMemoryCopy {
@Test(dataProvider = "slices")
public void testCopy(SegmentSlice s1, SegmentSlice s2) {
MemoryAddress addr1 = s1.segment.baseAddress();
MemoryAddress addr2 = s2.segment.baseAddress();
int size = Math.min(s1.size(), s2.size());
//prepare source and target segments
for (int i = 0 ; i < size ; i++) {
BYTE_HANDLE.set(addr2.addOffset(i), (byte)0);
BYTE_HANDLE.set(s2.segment.asSlice(i), (byte)0);
}
for (int i = 0 ; i < size ; i++) {
BYTE_HANDLE.set(addr1.addOffset(i), (byte) i);
BYTE_HANDLE.set(s1.segment.asSlice(i), (byte) i);
}
//perform copy
s2.segment.copyFrom(s1.segment.asSlice(0, size));
//check that copy actually worked
for (int i = 0 ; i < size ; i++) {
assertEquals((byte)i, BYTE_HANDLE.get(addr2.addOffset(i)));
assertEquals((byte)i, BYTE_HANDLE.get(s2.segment.asSlice(i)));
}
}

View File

@ -59,10 +59,10 @@ public class TestMemoryHandleAsUnsigned {
VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class);
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
intHandle.set(segment.baseAddress(), intValue);
intHandle.set(segment, intValue);
int expectedIntValue = Byte.toUnsignedInt(byteValue);
assertEquals((int) intHandle.get(segment.baseAddress()), expectedIntValue);
assertEquals((byte) byteHandle.get(segment.baseAddress()), byteValue);
assertEquals((int) intHandle.get(segment), expectedIntValue);
assertEquals((byte) byteHandle.get(segment), byteValue);
}
}
@ -81,10 +81,10 @@ public class TestMemoryHandleAsUnsigned {
VarHandle longHandle = MemoryHandles.asUnsigned(byteHandle, long.class);
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
longHandle.set(segment.baseAddress(), longValue);
longHandle.set(segment, longValue);
long expectedLongValue = Byte.toUnsignedLong(byteValue);
assertEquals((long) longHandle.get(segment.baseAddress()), expectedLongValue);
assertEquals((byte) byteHandle.get(segment.baseAddress()), byteValue);
assertEquals((long) longHandle.get(segment), expectedLongValue);
assertEquals((byte) byteHandle.get(segment), byteValue);
}
}
@ -103,10 +103,10 @@ public class TestMemoryHandleAsUnsigned {
VarHandle intHandle = MemoryHandles.asUnsigned(shortHandle, int.class);
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
intHandle.set(segment.baseAddress(), intValue);
intHandle.set(segment, intValue);
int expectedIntValue = Short.toUnsignedInt(shortValue);
assertEquals((int) intHandle.get(segment.baseAddress()), expectedIntValue);
assertEquals((short) shortHandle.get(segment.baseAddress()), shortValue);
assertEquals((int) intHandle.get(segment), expectedIntValue);
assertEquals((short) shortHandle.get(segment), shortValue);
}
}
@ -125,10 +125,10 @@ public class TestMemoryHandleAsUnsigned {
VarHandle longHandle = MemoryHandles.asUnsigned(shortHandle, long.class);
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
longHandle.set(segment.baseAddress(), longValue);
longHandle.set(segment, longValue);
long expectedLongValue = Short.toUnsignedLong(shortValue);
assertEquals((long) longHandle.get(segment.baseAddress()), expectedLongValue);
assertEquals((short) shortHandle.get(segment.baseAddress()), shortValue);
assertEquals((long) longHandle.get(segment), expectedLongValue);
assertEquals((short) shortHandle.get(segment), shortValue);
}
}
@ -151,10 +151,10 @@ public class TestMemoryHandleAsUnsigned {
VarHandle longHandle = MemoryHandles.asUnsigned(intHandle, long.class);
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
longHandle.set(segment.baseAddress(), longValue);
longHandle.set(segment, longValue);
long expectedLongValue = Integer.toUnsignedLong(intValue);
assertEquals((long) longHandle.get(segment.baseAddress()), expectedLongValue);
assertEquals((int) intHandle.get(segment.baseAddress()), intValue);
assertEquals((long) longHandle.get(segment), expectedLongValue);
assertEquals((int) intHandle.get(segment), intValue);
}
}
@ -165,10 +165,10 @@ public class TestMemoryHandleAsUnsigned {
VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class);
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
intHandle.set(segment.baseAddress(), 0L, (int) -1);
assertEquals((int) intHandle.get(segment.baseAddress(), 0L), 255);
intHandle.set(segment.baseAddress(), 1L, (int) 200);
assertEquals((int) intHandle.get(segment.baseAddress(), 1L), 200);
intHandle.set(segment, 0L, (int) -1);
assertEquals((int) intHandle.get(segment, 0L), 255);
intHandle.set(segment, 1L, (int) 200);
assertEquals((int) intHandle.get(segment, 1L), 200);
}
}
@ -176,19 +176,18 @@ public class TestMemoryHandleAsUnsigned {
public void testCoordinatesStride() {
byte[] arr = { 0, 0, (byte) 129, 0 };
MemorySegment segment = MemorySegment.ofArray(arr);
MemoryAddress addr = segment.baseAddress();
{
VarHandle byteHandle = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
.varHandle(byte.class, PathElement.sequenceElement());
VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class);
VarHandle strideHandle = MemoryHandles.withStride(intHandle, 1);
assertEquals((int) strideHandle.get(addr, 2L), 129);
assertEquals((int) intHandle.get(segment, 2L), 129);
}
{
VarHandle byteHandle = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
VarHandle strideHandle = MemoryHandles.withStride(byteHandle, 1);
VarHandle intHandle = MemoryHandles.asUnsigned(strideHandle, int.class);
assertEquals((int) intHandle.get(addr, 2L), 129);
VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
.varHandle(byte.class, PathElement.sequenceElement());
VarHandle intHandle = MemoryHandles.asUnsigned(byteHandle, int.class);
assertEquals((int) intHandle.get(segment, 2L), 129);
}
}

View File

@ -47,9 +47,8 @@ public class TestMismatch {
// stores a increasing sequence of values into the memory of the given segment
static MemorySegment initializeSegment(MemorySegment segment) {
MemoryAddress addr = segment.baseAddress();
for (int i = 0 ; i < segment.byteSize() ; i++) {
BYTE_HANDLE.set(addr.addOffset(i), (byte)i);
BYTE_HANDLE.set(segment.asSlice(i), (byte)i);
}
return segment;
}
@ -81,7 +80,7 @@ public class TestMismatch {
for (long i = s2.byteSize() -1 ; i >= 0; i--) {
long expectedMismatchOffset = i;
BYTE_HANDLE.set(s2.baseAddress().addOffset(i), (byte) 0xFF);
BYTE_HANDLE.set(s2.asSlice(i), (byte) 0xFF);
if (s1.byteSize() == s2.byteSize()) {
assertEquals(s1.mismatch(s2), expectedMismatchOffset);
@ -111,15 +110,18 @@ public class TestMismatch {
@Test
public void testLarge() {
try (var s1 = MemorySegment.allocateNative((long)Integer.MAX_VALUE + 10L);
var s2 = MemorySegment.allocateNative((long)Integer.MAX_VALUE + 10L)) {
assertEquals(s1.mismatch(s1), -1);
assertEquals(s1.mismatch(s2), -1);
assertEquals(s2.mismatch(s1), -1);
// skip if not on 64 bits
if (MemoryLayouts.ADDRESS.byteSize() > 32) {
try (var s1 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L);
var s2 = MemorySegment.allocateNative((long) Integer.MAX_VALUE + 10L)) {
assertEquals(s1.mismatch(s1), -1);
assertEquals(s1.mismatch(s2), -1);
assertEquals(s2.mismatch(s1), -1);
testLargeAcrossMaxBoundary(s1, s2);
testLargeAcrossMaxBoundary(s1, s2);
testLargeMismatchAcrossMaxBoundary(s1, s2);
testLargeMismatchAcrossMaxBoundary(s1, s2);
}
}
}
@ -135,7 +137,7 @@ public class TestMismatch {
private void testLargeMismatchAcrossMaxBoundary(MemorySegment s1, MemorySegment s2) {
for (long i = s2.byteSize() -1 ; i >= Integer.MAX_VALUE - 10L; i--) {
BYTE_HANDLE.set(s2.baseAddress().addOffset(i), (byte) 0xFF);
BYTE_HANDLE.set(s2.asSlice(i), (byte) 0xFF);
long expectedMismatchOffset = i;
assertEquals(s1.mismatch(s2), expectedMismatchOffset);
assertEquals(s2.mismatch(s1), expectedMismatchOffset);

View File

@ -24,18 +24,17 @@
/*
* @test
* @modules java.base/jdk.internal.misc
* jdk.incubator.foreign/jdk.internal.foreign
* @modules jdk.incubator.foreign/jdk.internal.foreign
* @run testng/othervm -Dforeign.restricted=permit TestNative
*/
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayout.PathElement;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import jdk.internal.misc.Unsafe;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@ -58,12 +57,6 @@ import static org.testng.Assert.*;
public class TestNative {
static Unsafe UNSAFE;
static {
UNSAFE = Unsafe.getUnsafe();
}
static SequenceLayout bytes = MemoryLayout.ofSequence(100,
MemoryLayouts.JAVA_BYTE.withOrder(ByteOrder.nativeOrder())
);
@ -100,24 +93,24 @@ public class TestNative {
static VarHandle longHandle = doubles.varHandle(long.class, PathElement.sequenceElement());
static VarHandle doubleHandle = longs.varHandle(double.class, PathElement.sequenceElement());
static void initBytes(MemoryAddress base, SequenceLayout seq, BiConsumer<MemoryAddress, Long> handleSetter) {
static void initBytes(MemorySegment base, SequenceLayout seq, BiConsumer<MemorySegment, Long> handleSetter) {
for (long i = 0; i < seq.elementCount().getAsLong() ; i++) {
handleSetter.accept(base, i);
}
}
static <Z extends Buffer> void checkBytes(MemoryAddress base, SequenceLayout layout,
BiFunction<MemoryAddress, Long, Object> handleExtractor,
static <Z extends Buffer> void checkBytes(MemorySegment base, SequenceLayout layout,
BiFunction<MemorySegment, Long, Object> handleExtractor,
Function<ByteBuffer, Z> bufferFactory,
BiFunction<Z, Integer, Object> nativeBufferExtractor,
BiFunction<Long, Integer, Object> nativeRawExtractor) {
long nelems = layout.elementCount().getAsLong();
ByteBuffer bb = base.segment().asSlice(base.segmentOffset(), (int)layout.byteSize()).asByteBuffer();
ByteBuffer bb = base.asByteBuffer();
Z z = bufferFactory.apply(bb);
for (long i = 0 ; i < nelems ; i++) {
Object handleValue = handleExtractor.apply(base, i);
Object bufferValue = nativeBufferExtractor.apply(z, (int)i);
Object rawValue = nativeRawExtractor.apply(base.toRawLongValue(), (int)i);
Object rawValue = nativeRawExtractor.apply(base.address().toRawLongValue(), (int)i);
if (handleValue instanceof Number) {
assertEquals(((Number)handleValue).longValue(), i);
assertEquals(((Number)bufferValue).longValue(), i);
@ -152,11 +145,10 @@ public class TestNative {
public static native void free(long address);
@Test(dataProvider="nativeAccessOps")
public void testNativeAccess(Consumer<MemoryAddress> checker, Consumer<MemoryAddress> initializer, SequenceLayout seq) {
public void testNativeAccess(Consumer<MemorySegment> checker, Consumer<MemorySegment> initializer, SequenceLayout seq) {
try (MemorySegment segment = MemorySegment.allocateNative(seq)) {
MemoryAddress address = segment.baseAddress();
initializer.accept(address);
checker.accept(address);
initializer.accept(segment);
checker.accept(segment);
}
}
@ -175,71 +167,79 @@ public class TestNative {
@Test
public void testDefaultAccessModes() {
MemoryAddress addr = MemoryAddress.ofLong(allocate(12));
MemorySegment mallocSegment = MemorySegment.ofNativeRestricted(addr, 12, null,
() -> free(addr.toRawLongValue()), null);
MemorySegment mallocSegment = addr.asSegmentRestricted(12, () -> free(addr.toRawLongValue()), null);
try (MemorySegment segment = mallocSegment) {
assertTrue(segment.hasAccessModes(ALL_ACCESS));
assertEquals(segment.accessModes(), ALL_ACCESS);
}
}
@Test
public void testDefaultAccessModesEverthing() {
MemorySegment everything = MemorySegment.ofNativeRestricted();
assertTrue(everything.hasAccessModes(READ | WRITE));
assertEquals(everything.accessModes(), READ | WRITE);
}
@Test
public void testMallocSegment() {
MemoryAddress addr = MemoryAddress.ofLong(allocate(12));
assertNull(addr.segment());
MemorySegment mallocSegment = MemorySegment.ofNativeRestricted(addr, 12, null,
() -> free(addr.toRawLongValue()), null);
MemorySegment mallocSegment = addr.asSegmentRestricted(12, () -> free(addr.toRawLongValue()), null);
assertEquals(mallocSegment.byteSize(), 12);
mallocSegment.close(); //free here
assertTrue(!mallocSegment.isAlive());
}
@Test
public void testEverythingSegment() {
MemoryAddress addr = MemoryAddress.ofLong(allocate(4));
MemorySegment everything = MemorySegment.ofNativeRestricted();
MemoryAccess.setIntAtOffset(everything, addr.toRawLongValue(), 42);
assertEquals(MemoryAccess.getIntAtOffset(everything, addr.toRawLongValue()), 42);
free(addr.toRawLongValue());
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadResize() {
try (MemorySegment segment = MemorySegment.allocateNative(4)) {
MemorySegment.ofNativeRestricted(segment.baseAddress(), 0, null, null, null);
segment.address().asSegmentRestricted(0);
}
}
@Test(expectedExceptions = NullPointerException.class)
public void testNullUnsafeSegment() {
MemorySegment.ofNativeRestricted(null, 10, null, null, null);
}
static {
System.loadLibrary("NativeAccess");
}
@DataProvider(name = "nativeAccessOps")
public Object[][] nativeAccessOps() {
Consumer<MemoryAddress> byteInitializer =
Consumer<MemorySegment> byteInitializer =
(base) -> initBytes(base, bytes, (addr, pos) -> byteHandle.set(addr, pos, (byte)(long)pos));
Consumer<MemoryAddress> charInitializer =
Consumer<MemorySegment> charInitializer =
(base) -> initBytes(base, chars, (addr, pos) -> charHandle.set(addr, pos, (char)(long)pos));
Consumer<MemoryAddress> shortInitializer =
Consumer<MemorySegment> shortInitializer =
(base) -> initBytes(base, shorts, (addr, pos) -> shortHandle.set(addr, pos, (short)(long)pos));
Consumer<MemoryAddress> intInitializer =
Consumer<MemorySegment> intInitializer =
(base) -> initBytes(base, ints, (addr, pos) -> intHandle.set(addr, pos, (int)(long)pos));
Consumer<MemoryAddress> floatInitializer =
Consumer<MemorySegment> floatInitializer =
(base) -> initBytes(base, floats, (addr, pos) -> floatHandle.set(addr, pos, (float)(long)pos));
Consumer<MemoryAddress> longInitializer =
Consumer<MemorySegment> longInitializer =
(base) -> initBytes(base, longs, (addr, pos) -> longHandle.set(addr, pos, (long)pos));
Consumer<MemoryAddress> doubleInitializer =
Consumer<MemorySegment> doubleInitializer =
(base) -> initBytes(base, doubles, (addr, pos) -> doubleHandle.set(addr, pos, (double)(long)pos));
Consumer<MemoryAddress> byteChecker =
Consumer<MemorySegment> byteChecker =
(base) -> checkBytes(base, bytes, byteHandle::get, bb -> bb, TestNative::getByteBuffer, TestNative::getByteRaw);
Consumer<MemoryAddress> charChecker =
Consumer<MemorySegment> charChecker =
(base) -> checkBytes(base, chars, charHandle::get, ByteBuffer::asCharBuffer, TestNative::getCharBuffer, TestNative::getCharRaw);
Consumer<MemoryAddress> shortChecker =
Consumer<MemorySegment> shortChecker =
(base) -> checkBytes(base, shorts, shortHandle::get, ByteBuffer::asShortBuffer, TestNative::getShortBuffer, TestNative::getShortRaw);
Consumer<MemoryAddress> intChecker =
Consumer<MemorySegment> intChecker =
(base) -> checkBytes(base, ints, intHandle::get, ByteBuffer::asIntBuffer, TestNative::getIntBuffer, TestNative::getIntRaw);
Consumer<MemoryAddress> floatChecker =
Consumer<MemorySegment> floatChecker =
(base) -> checkBytes(base, floats, floatHandle::get, ByteBuffer::asFloatBuffer, TestNative::getFloatBuffer, TestNative::getFloatRaw);
Consumer<MemoryAddress> longChecker =
Consumer<MemorySegment> longChecker =
(base) -> checkBytes(base, longs, longHandle::get, ByteBuffer::asLongBuffer, TestNative::getLongBuffer, TestNative::getLongRaw);
Consumer<MemoryAddress> doubleChecker =
Consumer<MemorySegment> doubleChecker =
(base) -> checkBytes(base, doubles, doubleHandle::get, ByteBuffer::asDoubleBuffer, TestNative::getDoubleBuffer, TestNative::getDoubleRaw);
return new Object[][]{

View File

@ -29,8 +29,8 @@
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.MemorySegment;
import org.testng.annotations.Test;
public class TestNoForeignUnsafeOverride {
@ -40,6 +40,6 @@ public class TestNoForeignUnsafeOverride {
@Test(expectedExceptions = IllegalAccessError.class)
public void testUnsafeAccess() {
MemorySegment.ofNativeRestricted(MemoryAddress.ofLong(42), 10, null, null, null);
MemorySegment.ofNativeRestricted();
}
}

View File

@ -27,6 +27,7 @@
* @run testng TestRebase
*/
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
@ -44,35 +45,33 @@ import static org.testng.Assert.fail;
public class TestRebase {
static VarHandle BYTE_VH = MemoryLayouts.JAVA_BYTE.varHandle(byte.class);
@Test(dataProvider = "slices")
public void testRebase(SegmentSlice s1, SegmentSlice s2) {
if (s1.contains(s2)) {
//check that an address and its rebased counterpart point to same element
MemoryAddress base = s2.segment.baseAddress();
MemoryAddress rebased = base.rebase(s1.segment);
MemoryAddress base = s2.segment.address();
long offset = base.segmentOffset(s1.segment);
for (int i = 0; i < s2.size(); i++) {
int expected = (int) BYTE_VH.get(base.addOffset(i));
int found = (int) BYTE_VH.get(rebased.addOffset(i));
int expected = MemoryAccess.getByteAtOffset(s2.segment, i);
int found = (int)MemoryAccess.getByteAtOffset(s1.segment, i + offset);
assertEquals(found, expected);
}
} else if (s1.kind != s2.kind) {
// check that rebase s1 to s2 fails
try {
s1.segment.baseAddress().rebase(s2.segment);
s1.segment.address().segmentOffset(s2.segment);
fail("Rebase unexpectedly passed!");
} catch (IllegalArgumentException ex) {
assertTrue(true);
}
} else if (!s2.contains(s1)) {
//disjoint segments - check that rebased address is out of bounds
MemoryAddress base = s2.segment.baseAddress();
MemoryAddress rebased = base.rebase(s1.segment);
MemoryAddress base = s2.segment.address();
long offset = base.segmentOffset(s1.segment);
for (int i = 0; i < s2.size(); i++) {
BYTE_VH.get(base.addOffset(i));
MemoryAccess.getByteAtOffset(s2.segment, i);
try {
BYTE_VH.get(rebased.addOffset(i));
MemoryAccess.getByteAtOffset(s1.segment, i + offset);
fail("Rebased address on a disjoint segment is not out of bounds!");
} catch (IndexOutOfBoundsException ex) {
assertTrue(true);
@ -129,7 +128,7 @@ public class TestRebase {
//init root segment
MemorySegment segment = kind.makeSegment(16);
for (int i = 0 ; i < 16 ; i++) {
BYTE_VH.set(segment.baseAddress().addOffset(i), (byte)i);
MemoryAccess.setByteAtOffset(segment, i, (byte)i);
}
//compute all slices
for (int size : sizes) {

View File

@ -23,10 +23,9 @@
/*
* @test
* @run testng TestSegments
* @run testng/othervm -XX:MaxDirectMemorySize=1M TestSegments
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
@ -34,13 +33,13 @@ import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.invoke.VarHandle;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.LongFunction;
@ -74,9 +73,6 @@ public class TestSegments {
Thread t = new Thread(() -> {
try {
Object o = member.method.invoke(segment, member.params);
if (member.method.getName().equals("acquire")) {
((MemorySegment)o).close();
}
} catch (ReflectiveOperationException ex) {
throw new IllegalStateException(ex);
}
@ -88,38 +84,40 @@ public class TestSegments {
}
}
@Test(dataProvider = "segmentOperations")
public void testOpAfterClose(SegmentMember member) throws Throwable {
MemorySegment segment = MemorySegment.allocateNative(4);
segment.close();
try {
Object o = member.method.invoke(segment, member.params);
assertFalse(member.isConfined());
} catch (InvocationTargetException ex) {
assertTrue(member.isConfined());
Throwable target = ex.getTargetException();
assertTrue(target instanceof NullPointerException ||
target instanceof UnsupportedOperationException ||
target instanceof IllegalStateException);
}
}
@Test(expectedExceptions = OutOfMemoryError.class)
public void testNativeAllocationTooBig() {
try (MemorySegment segment = MemorySegment.allocateNative(1024 * 1024 * 8 * 2)) { // 2M
// do nothing
}
}
@Test
public void testNativeSegmentIsZeroed() {
VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
.varHandle(byte.class, MemoryLayout.PathElement.sequenceElement());
try (MemorySegment segment = MemorySegment.allocateNative(1000)) {
for (long i = 0 ; i < segment.byteSize() ; i++) {
assertEquals(0, (byte)byteHandle.get(segment.baseAddress(), i));
assertEquals(0, (byte)byteHandle.get(segment, i));
}
}
}
@Test
public void testNothingSegmentAccess() {
VarHandle longHandle = MemoryLayouts.JAVA_LONG.varHandle(long.class);
long[] values = { 0L, Integer.MAX_VALUE - 1, (long) Integer.MAX_VALUE + 1 };
for (long value : values) {
MemoryAddress addr = MemoryAddress.ofLong(value);
try {
longHandle.get(addr);
} catch (UnsupportedOperationException ex) {
assertTrue(ex.getMessage().contains("Required access mode"));
}
}
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testNothingSegmentOffset() {
MemoryAddress addr = MemoryAddress.ofLong(42);
assertNull(addr.segment());
addr.segmentOffset();
}
@Test
public void testSlices() {
VarHandle byteHandle = MemoryLayout.ofSequence(MemoryLayouts.JAVA_BYTE)
@ -127,21 +125,16 @@ public class TestSegments {
try (MemorySegment segment = MemorySegment.allocateNative(10)) {
//init
for (byte i = 0 ; i < segment.byteSize() ; i++) {
byteHandle.set(segment.baseAddress(), (long)i, i);
byteHandle.set(segment, (long)i, i);
}
long start = 0;
MemoryAddress base = segment.baseAddress();
MemoryAddress last = base.addOffset(10);
while (!base.equals(last)) {
MemorySegment slice = segment.asSlice(base.segmentOffset(), 10 - start);
for (long i = start ; i < 10 ; i++) {
for (int offset = 0 ; offset < 10 ; offset++) {
MemorySegment slice = segment.asSlice(offset);
for (long i = offset ; i < 10 ; i++) {
assertEquals(
byteHandle.get(segment.baseAddress(), i),
byteHandle.get(slice.baseAddress(), i - start)
byteHandle.get(segment, i),
byteHandle.get(slice, i - offset)
);
}
base = base.addOffset(1);
start++;
}
}
}
@ -197,20 +190,20 @@ public class TestSegments {
try (MemorySegment segment = memorySegmentSupplier.get()) {
segment.fill(value);
for (long l = 0; l < segment.byteSize(); l++) {
assertEquals((byte) byteHandle.get(segment.baseAddress(), l), value);
assertEquals((byte) byteHandle.get(segment, l), value);
}
// fill a slice
var sliceSegment = segment.asSlice(1, segment.byteSize() - 2).fill((byte) ~value);
for (long l = 0; l < sliceSegment.byteSize(); l++) {
assertEquals((byte) byteHandle.get(sliceSegment.baseAddress(), l), ~value);
assertEquals((byte) byteHandle.get(sliceSegment, l), ~value);
}
// assert enclosing slice
assertEquals((byte) byteHandle.get(segment.baseAddress(), 0L), value);
assertEquals((byte) byteHandle.get(segment, 0L), value);
for (long l = 1; l < segment.byteSize() - 2; l++) {
assertEquals((byte) byteHandle.get(segment.baseAddress(), l), (byte) ~value);
assertEquals((byte) byteHandle.get(segment, l), (byte) ~value);
}
assertEquals((byte) byteHandle.get(segment.baseAddress(), segment.byteSize() - 1L), value);
assertEquals((byte) byteHandle.get(segment, segment.byteSize() - 1L), value);
}
}
}
@ -319,8 +312,9 @@ public class TestSegments {
static Object[][] segmentMembers() {
List<SegmentMember> members = new ArrayList<>();
for (Method m : MemorySegment.class.getDeclaredMethods()) {
//skip statics and method declared in j.l.Object
if (m.getDeclaringClass().equals(Object.class) ||
//skip defaults, statics and method declared in j.l.Object
if (m.isDefault() ||
m.getDeclaringClass().equals(Object.class) ||
(m.getModifiers() & Modifier.STATIC) != 0) continue;
Object[] args = Stream.of(m.getParameterTypes())
.map(TestSegments::defaultValue)
@ -335,12 +329,22 @@ public class TestSegments {
final Object[] params;
final static List<String> CONFINED_NAMES = List.of(
"address",
"close",
"share",
"handoff",
"registerCleaner",
"fill",
"spliterator",
"copyFrom",
"mismatch",
"toByteArray",
"withOwnerThread"
"toCharArray",
"toShortArray",
"toIntArray",
"toFloatArray",
"toLongArray",
"toDoubleArray"
);
public SegmentMember(Method method, Object[] params) {
@ -395,30 +399,10 @@ public class TestSegments {
}
enum AccessActions {
ACQUIRE(MemorySegment.ACQUIRE) {
SHARE(MemorySegment.SHARE) {
@Override
void run(MemorySegment segment) {
Spliterator<MemorySegment> spliterator =
MemorySegment.spliterator(segment, MemoryLayout.ofSequence(segment.byteSize(), MemoryLayouts.JAVA_BYTE));
AtomicReference<RuntimeException> exception = new AtomicReference<>();
Runnable action = () -> {
try {
spliterator.tryAdvance(s -> { });
} catch (RuntimeException e) {
exception.set(e);
}
};
Thread thread = new Thread(action);
thread.start();
try {
thread.join();
} catch (InterruptedException ex) {
throw new AssertionError(ex);
}
RuntimeException e = exception.get();
if (e != null) {
throw e;
}
segment.share();
}
},
CLOSE(MemorySegment.CLOSE) {
@ -430,19 +414,19 @@ public class TestSegments {
READ(MemorySegment.READ) {
@Override
void run(MemorySegment segment) {
INT_HANDLE.get(segment.baseAddress());
INT_HANDLE.get(segment);
}
},
WRITE(MemorySegment.WRITE) {
@Override
void run(MemorySegment segment) {
INT_HANDLE.set(segment.baseAddress(), 42);
INT_HANDLE.set(segment, 42);
}
},
HANDOFF(MemorySegment.HANDOFF) {
@Override
void run(MemorySegment segment) {
segment.withOwnerThread(new Thread());
segment.handoff(new Thread());
}
};

View File

@ -27,12 +27,8 @@
* @run testng/othervm -Dforeign.restricted=permit TestSharedAccess
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SequenceLayout;
import org.testng.annotations.Test;
import jdk.incubator.foreign.*;
import org.testng.annotations.*;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
@ -41,14 +37,10 @@ import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import static org.testng.Assert.*;
public class TestSharedAccess {
@ -59,17 +51,17 @@ public class TestSharedAccess {
Thread owner = Thread.currentThread();
MemorySegment s = MemorySegment.allocateNative(4);
AtomicReference<MemorySegment> confined = new AtomicReference<>(s);
setInt(s.baseAddress(), 42);
assertEquals(getInt(s.baseAddress()), 42);
setInt(s, 42);
assertEquals(getInt(s), 42);
List<Thread> threads = new ArrayList<>();
for (int i = 0 ; i < 1000 ; i++) {
threads.add(new Thread(() -> {
assertEquals(getInt(confined.get().baseAddress()), 42);
confined.set(confined.get().withOwnerThread(owner));
assertEquals(getInt(confined.get()), 42);
confined.set(confined.get().handoff(owner));
}));
}
threads.forEach(t -> {
confined.set(confined.get().withOwnerThread(t));
confined.set(confined.get().handoff(t));
t.start();
try {
t.join();
@ -83,13 +75,13 @@ public class TestSharedAccess {
@Test
public void testShared() throws Throwable {
SequenceLayout layout = MemoryLayout.ofSequence(1024, MemoryLayouts.JAVA_INT);
try (MemorySegment s = MemorySegment.allocateNative(layout)) {
try (MemorySegment s = MemorySegment.allocateNative(layout).share()) {
for (int i = 0 ; i < layout.elementCount().getAsLong() ; i++) {
setInt(s.baseAddress().addOffset(i * 4), 42);
setInt(s.asSlice(i * 4), 42);
}
List<Thread> threads = new ArrayList<>();
List<Spliterator<MemorySegment>> spliterators = new ArrayList<>();
spliterators.add(MemorySegment.spliterator(s, layout));
spliterators.add(s.spliterator(layout));
while (true) {
boolean progress = false;
List<Spliterator<MemorySegment>> newSpliterators = new ArrayList<>();
@ -108,7 +100,7 @@ public class TestSharedAccess {
for (Spliterator<MemorySegment> spliterator : spliterators) {
threads.add(new Thread(() -> {
spliterator.tryAdvance(local -> {
assertEquals(getInt(local.baseAddress()), 42);
assertEquals(getInt(local), 42);
accessCount.incrementAndGet();
});
}));
@ -128,14 +120,13 @@ public class TestSharedAccess {
@Test
public void testSharedUnsafe() throws Throwable {
try (MemorySegment s = MemorySegment.allocateNative(4)) {
setInt(s.baseAddress(), 42);
assertEquals(getInt(s.baseAddress()), 42);
setInt(s, 42);
assertEquals(getInt(s), 42);
List<Thread> threads = new ArrayList<>();
MemorySegment sharedSegment = MemorySegment.ofNativeRestricted(
s.baseAddress(), s.byteSize(), null, null, null);
MemorySegment sharedSegment = s.address().asSegmentRestricted(s.byteSize()).share();
for (int i = 0 ; i < 1000 ; i++) {
threads.add(new Thread(() -> {
assertEquals(getInt(sharedSegment.baseAddress()), 42);
assertEquals(getInt(sharedSegment), 42);
}));
}
threads.forEach(Thread::start);
@ -149,65 +140,32 @@ public class TestSharedAccess {
}
}
@Test(expectedExceptions=IllegalStateException.class)
public void testBadCloseWithPendingAcquire() {
withAcquired(MemorySegment::close);
@Test
public void testHandoffToSelf() {
MemorySegment s1 = MemorySegment.ofArray(new int[4]);
MemorySegment s2 = s1.handoff(Thread.currentThread());
assertFalse(s1.isAlive());
assertTrue(s2.isAlive());
}
@Test(expectedExceptions=IllegalStateException.class)
public void testBadCloseWithPendingAcquireBuffer() {
withAcquired(segment -> {
segment = MemorySegment.ofByteBuffer(segment.asByteBuffer()); // original segment is lost
segment.close(); // this should still fail
});
@Test
public void testShareTwice() {
MemorySegment s1 = MemorySegment.ofArray(new int[4]).share();
MemorySegment s2 = s1.share();
assertFalse(s1.isAlive());
assertTrue(s2.isAlive());
}
@Test(expectedExceptions=IllegalStateException.class)
public void testBadHandoffWithPendingAcquire() {
withAcquired(segment -> segment.withOwnerThread(new Thread()));
@Test(expectedExceptions=UnsupportedOperationException.class)
public void testBadHandoffNoAccess() {
MemorySegment.ofArray(new int[4])
.withAccessModes(MemorySegment.CLOSE).handoff(new Thread());
}
@Test(expectedExceptions=IllegalStateException.class)
public void testBadHandoffWithPendingAcquireBuffer() {
withAcquired(segment -> {
segment = MemorySegment.ofByteBuffer(segment.asByteBuffer()); // original segment is lost
segment.withOwnerThread(new Thread()); // this should still fail
});
}
@Test(expectedExceptions=IllegalArgumentException.class)
public void testBadHandoffSameThread() {
MemorySegment.ofArray(new int[4]).withOwnerThread(Thread.currentThread());
}
@Test(expectedExceptions=NullPointerException.class)
public void testBadHandoffNullThread() {
MemorySegment.ofArray(new int[4]).withOwnerThread(null);
}
private void withAcquired(Consumer<MemorySegment> acquiredAction) {
CountDownLatch holder = new CountDownLatch(1);
MemorySegment segment = MemorySegment.allocateNative(16);
Spliterator<MemorySegment> spliterator = MemorySegment.spliterator(segment,
MemoryLayout.ofSequence(16, MemoryLayouts.JAVA_BYTE));
CountDownLatch acquired = new CountDownLatch(1);
Runnable r = () -> spliterator.tryAdvance(s -> {
try {
acquired.countDown();
holder.await();
} catch (InterruptedException ex) {
throw new AssertionError(ex);
}
});
new Thread(r).start();
try {
acquired.await();
acquiredAction.accept(segment);
} catch (InterruptedException ex) {
throw new AssertionError(ex);
} finally {
holder.countDown();
}
@Test(expectedExceptions=UnsupportedOperationException.class)
public void testBadShareNoAccess() {
MemorySegment.ofArray(new int[4])
.withAccessModes(MemorySegment.CLOSE).share();
}
@Test
@ -228,8 +186,7 @@ public class TestSharedAccess {
} catch (InterruptedException e) {
}
MemoryAddress base = s2.baseAddress();
setInt(base.addOffset(4), -42);
setInt(s2.asSlice(4), -42);
fail();
} catch (IllegalStateException ex) {
assertTrue(ex.getMessage().contains("owning thread"));
@ -237,19 +194,18 @@ public class TestSharedAccess {
});
a.await();
MemoryAddress base = s1.baseAddress();
setInt(base.addOffset(4), 42);
setInt(s1.asSlice(4), 42);
}
b.countDown();
r.get();
}
static int getInt(MemoryAddress address) {
return (int)intHandle.getVolatile(address);
static int getInt(MemorySegment base) {
return (int)intHandle.getVolatile(base);
}
static void setInt(MemoryAddress address, int value) {
intHandle.setVolatile(address, value);
static void setInt(MemorySegment base, int value) {
intHandle.setVolatile(base, value);
}
}

View File

@ -44,15 +44,13 @@ public class TestSlices {
static VarHandle VH_ALL = LAYOUT.varHandle(int.class,
MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.sequenceElement());
static VarHandle VH_INT = MemoryLayouts.JAVA_INT.varHandle(int.class);
@Test(dataProvider = "slices")
public void testSlices(VarHandle handle, int lo, int hi, int[] values) {
try (MemorySegment segment = MemorySegment.allocateNative(LAYOUT)) {
//init
for (long i = 0 ; i < 2 ; i++) {
for (long j = 0 ; j < 5 ; j++) {
VH_ALL.set(segment.baseAddress(), i, j, (int)j + 1 + ((int)i * 5));
VH_ALL.set(segment, i, j, (int)j + 1 + ((int)i * 5));
}
}
@ -64,7 +62,7 @@ public class TestSlices {
int index = 0;
for (long i = 0 ; i < i_max ; i++) {
for (long j = 0 ; j < j_max ; j++) {
int x = (int) handle.get(segment.baseAddress(), i, j);
int x = (int) handle.get(segment, i, j);
assertEquals(x, values[index++]);
}
}
@ -79,19 +77,15 @@ public class TestSlices {
// x[0::2]
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.sequenceElement(0, 2)), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } },
{ MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, 8), 20), 2, 3, new int[] { 1, 3, 5, 6, 8, 10 } },
// x[1::2]
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.sequenceElement(1, 2)), 2, 2, new int[] { 2, 4, 7, 9 } },
{ MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, 8), 20), 4), 2, 2, new int[] { 2, 4, 7, 9 } },
// x[4::-2]
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.sequenceElement(4, -2)), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } },
{ MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, -8), 20), 16), 2, 3, new int[] { 5, 3, 1, 10, 8, 6 } },
// x[3::-2]
{ LAYOUT.varHandle(int.class, MemoryLayout.PathElement.sequenceElement(),
MemoryLayout.PathElement.sequenceElement(3, -2)), 2, 2, new int[] { 4, 2, 9, 7 } },
{ MemoryHandles.withOffset(MemoryHandles.withStride(MemoryHandles.withStride(VH_INT, -8), 20), 12), 2, 2, new int[] { 4, 2, 9, 7 } },
};
}
}

View File

@ -26,7 +26,6 @@
* @run testng TestSpliterator
*/
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
@ -61,22 +60,22 @@ public class TestSpliterator {
SequenceLayout layout = MemoryLayout.ofSequence(size, MemoryLayouts.JAVA_INT);
//setup
MemorySegment segment = MemorySegment.allocateNative(layout);
MemorySegment segment = MemorySegment.allocateNative(layout).share();
for (int i = 0; i < layout.elementCount().getAsLong(); i++) {
INT_HANDLE.set(segment.baseAddress(), (long) i, i);
INT_HANDLE.set(segment, (long) i, i);
}
long expected = LongStream.range(0, layout.elementCount().getAsLong()).sum();
//serial
long serial = sum(0, segment);
assertEquals(serial, expected);
//parallel counted completer
long parallelCounted = new SumSegmentCounted(null, MemorySegment.spliterator(segment, layout), threshold).invoke();
long parallelCounted = new SumSegmentCounted(null, segment.spliterator(layout), threshold).invoke();
assertEquals(parallelCounted, expected);
//parallel recursive action
long parallelRecursive = new SumSegmentRecursive(MemorySegment.spliterator(segment, layout), threshold).invoke();
long parallelRecursive = new SumSegmentRecursive(segment.spliterator(layout), threshold).invoke();
assertEquals(parallelRecursive, expected);
//parallel stream
long streamParallel = StreamSupport.stream(MemorySegment.spliterator(segment, layout), true)
long streamParallel = StreamSupport.stream(segment.spliterator(layout), true)
.reduce(0L, TestSpliterator::sumSingle, Long::sum);
assertEquals(streamParallel, expected);
segment.close();
@ -88,27 +87,26 @@ public class TestSpliterator {
//setup
MemorySegment segment = MemorySegment.allocateNative(layout);
for (int i = 0; i < layout.elementCount().getAsLong(); i++) {
INT_HANDLE.set(segment.baseAddress(), (long) i, i);
INT_HANDLE.set(segment, (long) i, i);
}
long expected = LongStream.range(0, layout.elementCount().getAsLong()).sum();
//check that a segment w/o ACQUIRE access mode can still be used from same thread
AtomicLong spliteratorSum = new AtomicLong();
spliterator(segment.withAccessModes(MemorySegment.READ), layout)
segment.withAccessModes(MemorySegment.READ).spliterator(layout)
.forEachRemaining(s -> spliteratorSum.addAndGet(sumSingle(0L, s)));
assertEquals(spliteratorSum.get(), expected);
}
static long sumSingle(long acc, MemorySegment segment) {
return acc + (int)INT_HANDLE.get(segment.baseAddress(), 0L);
return acc + (int)INT_HANDLE.get(segment, 0L);
}
static long sum(long start, MemorySegment segment) {
long sum = start;
MemoryAddress base = segment.baseAddress();
int length = (int)segment.byteSize();
for (int i = 0 ; i < length / CARRIER_SIZE ; i++) {
sum += (int)INT_HANDLE.get(base, (long)i);
sum += (int)INT_HANDLE.get(segment, (long)i);
}
return sum;
}
@ -211,13 +209,13 @@ public class TestSpliterator {
var mallocSegment = MemorySegment.allocateNative(layout);
Map<Supplier<Spliterator<MemorySegment>>,Integer> l = Map.of(
() -> spliterator(mallocSegment.withAccessModes(ALL_ACCESS), layout), ALL_ACCESS,
() -> spliterator(mallocSegment.withAccessModes(0), layout), 0,
() -> spliterator(mallocSegment.withAccessModes(READ), layout), READ,
() -> spliterator(mallocSegment.withAccessModes(CLOSE), layout), 0,
() -> spliterator(mallocSegment.withAccessModes(READ|WRITE), layout), READ|WRITE,
() -> spliterator(mallocSegment.withAccessModes(READ|WRITE|ACQUIRE), layout), READ|WRITE|ACQUIRE,
() -> spliterator(mallocSegment.withAccessModes(READ|WRITE|ACQUIRE|HANDOFF), layout), READ|WRITE|ACQUIRE|HANDOFF
() -> mallocSegment.withAccessModes(ALL_ACCESS).spliterator(layout), ALL_ACCESS,
() -> mallocSegment.withAccessModes(0).spliterator(layout), 0,
() -> mallocSegment.withAccessModes(READ).spliterator(layout), READ,
() -> mallocSegment.withAccessModes(CLOSE).spliterator(layout), 0,
() -> mallocSegment.withAccessModes(READ|WRITE).spliterator(layout), READ|WRITE,
() -> mallocSegment.withAccessModes(READ|WRITE| SHARE).spliterator(layout), READ|WRITE| SHARE,
() -> mallocSegment.withAccessModes(READ|WRITE| SHARE |HANDOFF).spliterator(layout), READ|WRITE| SHARE |HANDOFF
);
return l.entrySet().stream().map(e -> new Object[] { e.getKey(), e.getValue() }).toArray(Object[][]::new);

View File

@ -31,7 +31,6 @@ import jdk.incubator.foreign.MemoryHandles;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import java.lang.invoke.MethodHandles;
@ -45,36 +44,20 @@ public class TestVarHandleCombinators {
@Test
public void testElementAccess() {
VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
vh = MemoryHandles.withStride(vh, 1);
byte[] arr = { 0, 0, -1, 0 };
MemorySegment segment = MemorySegment.ofArray(arr);
MemoryAddress addr = segment.baseAddress();
assertEquals((byte) vh.get(addr, 2), (byte) -1);
assertEquals((byte) vh.get(segment, 2), (byte) -1);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testUnalignedElement() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder());
vh = MemoryHandles.withStride(vh, 2);
MemorySegment segment = MemorySegment.ofArray(new byte[4]);
vh.get(segment.baseAddress(), 1L); //should throw
}
public void testZeroStrideElement() {
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
VarHandle strided_vh = MemoryHandles.withStride(vh, 0);
MemorySegment segment = MemorySegment.ofArray(new int[] { 42 });
for (int i = 0 ; i < 100 ; i++) {
assertEquals((int)vh.get(segment.baseAddress()), strided_vh.get(segment.baseAddress(), (long)i));
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testStrideWrongHandle() {
VarHandle vh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.nativeOrder());
MemoryHandles.withStride(vh, 10);
vh.get(segment, 2L); //should throw
//FIXME: the VH only checks the alignment of the segment, which is fine if the VH is derived from layouts,
//FIXME: but not if the VH is just created from scratch - we need a VH variable to govern this property,
//FIXME: at least until the VM is fixed
}
@Test(expectedExceptions = IllegalArgumentException.class)
@ -92,56 +75,8 @@ public class TestVarHandleCombinators {
VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder());
MemorySegment segment = MemorySegment.allocateNative(1, 2);
MemoryAddress address = segment.baseAddress();
vh.set(address, (byte) 10); // fine, memory region is aligned
assertEquals((byte) vh.get(address), (byte) 10);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testAlignBadAccess() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 2, ByteOrder.nativeOrder());
vh = MemoryHandles.withOffset(vh, 1); // offset by 1 byte
MemorySegment segment = MemorySegment.allocateNative(2, 2);
MemoryAddress address = segment.baseAddress();
vh.set(address, (byte) 10); // should be bad align
}
public void testZeroOffsetElement() {
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
VarHandle offset_vh = MemoryHandles.withOffset(vh, 0);
MemorySegment segment = MemorySegment.ofArray(new int[] { 42 });
for (int i = 0 ; i < 100 ; i++) {
assertEquals((int)vh.get(segment.baseAddress()), offset_vh.get(segment.baseAddress(), (long)i));
}
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testOffsetWrongHandle() {
VarHandle vh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.nativeOrder());
MemoryHandles.withOffset(vh, 1);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testUnalignedOffset() {
VarHandle vh = MemoryHandles.varHandle(byte.class, 4, ByteOrder.nativeOrder());
vh = MemoryHandles.withOffset(vh, 2);
MemorySegment segment = MemorySegment.ofArray(new byte[4]);
vh.get(segment.baseAddress()); //should throw
}
@Test
public void testOffset() {
VarHandle vh = MemoryHandles.varHandle(byte.class, ByteOrder.nativeOrder());
vh = MemoryHandles.withOffset(vh, 1);
MemorySegment segment = MemorySegment.ofArray(new byte[2]);
MemoryAddress address = segment.baseAddress();
vh.set(address, (byte) 10);
assertEquals((byte) vh.get(address), (byte) 10);
vh.set(segment, 0L, (byte) 10); // fine, memory region is aligned
assertEquals((byte) vh.get(segment, 0L), (byte) 10);
}
@Test
@ -149,9 +84,7 @@ public class TestVarHandleCombinators {
VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.LITTLE_ENDIAN);
byte[] arr = new byte[2];
MemorySegment segment = MemorySegment.ofArray(arr);
MemoryAddress address = segment.baseAddress();
vh.set(address, (short) 0xFF);
vh.set(segment, 0L, (short) 0xFF);
assertEquals(arr[0], (byte) 0xFF);
assertEquals(arr[1], (byte) 0);
}
@ -161,9 +94,7 @@ public class TestVarHandleCombinators {
VarHandle vh = MemoryHandles.varHandle(short.class, 2, ByteOrder.BIG_ENDIAN);
byte[] arr = new byte[2];
MemorySegment segment = MemorySegment.ofArray(arr);
MemoryAddress address = segment.baseAddress();
vh.set(address, (short) 0xFF);
vh.set(segment, 0L, (short) 0xFF);
assertEquals(arr[0], (byte) 0);
assertEquals(arr[1], (byte) 0xFF);
}
@ -176,16 +107,13 @@ public class TestVarHandleCombinators {
//[10 : [5 : [x32 i32]]]
VarHandle vh = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
vh = MemoryHandles.withOffset(vh, 4);
VarHandle inner_vh = MemoryHandles.withStride(vh, 8);
VarHandle outer_vh = MemoryHandles.withStride(inner_vh, 5 * 8);
int count = 0;
try (MemorySegment segment = MemorySegment.allocateNative(inner_size * outer_size * 8)) {
for (long i = 0; i < outer_size; i++) {
for (long j = 0; j < inner_size; j++) {
outer_vh.set(segment.baseAddress(), i, j, count);
vh.set(segment, i * 40 + j * 8, count);
assertEquals(
(int)inner_vh.get(segment.baseAddress().addOffset(i * inner_size * 8), j),
(int)vh.get(segment.asSlice(i * inner_size * 8), j * 8),
count);
count++;
}
@ -205,7 +133,7 @@ public class TestVarHandleCombinators {
{ boolean.class },
{ Object.class },
{ int[].class },
{ MemoryAddress.class }
{ MemorySegment.class }
};
}

View File

@ -230,10 +230,9 @@ public class VarHandleTestExact {
VarHandle vh = MemoryHandles.varHandle(carrier, ByteOrder.nativeOrder());
assertFalse(vh.hasInvokeExactBehavior());
try (MemorySegment seg = MemorySegment.allocateNative(8)) {
MemoryAddress base = seg.baseAddress();
try {
vh.set(base, testValue);
vh.withInvokeBehavior().set(base, testValue);
vh.set(seg, 0L, testValue);
vh.withInvokeBehavior().set(seg, 0L, testValue);
} catch (WrongMethodTypeException wmte) {
fail("Unexpected exception", wmte);
}
@ -241,11 +240,11 @@ public class VarHandleTestExact {
vh = vh.withInvokeExactBehavior();
assertTrue(vh.hasInvokeExactBehavior());
try {
setter.set(vh, base, testValue); // should throw
setter.set(vh, seg, 0L, testValue); // should throw
fail("Exception expected");
} catch (WrongMethodTypeException wmte) {
assertMatches(wmte.getMessage(),
".*\\Qexpected (MemoryAddress," + carrier.getSimpleName() + ")void \\E.*");
".*\\Qexpected (MemorySegment,long," + carrier.getSimpleName() + ")void \\E.*");
}
}
}
@ -281,7 +280,7 @@ public class VarHandleTestExact {
}
private interface SetSegmentX {
void set(VarHandle vh, MemoryAddress addr, Object testValue);
void set(VarHandle vh, MemorySegment segment, long offser, Object testValue);
}
private static void consume(Object o) {}
@ -418,11 +417,11 @@ public class VarHandleTestExact {
List<Object[]> cases = new ArrayList<>();
// create a bunch of different sig-poly call sites
testCaseSegmentSet(cases, long.class, 1234, (vh, addr, tv) -> vh.set(addr, (int) tv));
testCaseSegmentSet(cases, long.class, (char) 1234, (vh, addr, tv) -> vh.set(addr, (char) tv));
testCaseSegmentSet(cases, long.class, (short) 1234, (vh, addr, tv) -> vh.set(addr, (short) tv));
testCaseSegmentSet(cases, long.class, (byte) 1234, (vh, addr, tv) -> vh.set(addr, (byte) tv));
testCaseSegmentSet(cases, double.class, 1234F, (vh, addr, tv) -> vh.set(addr, (float) tv));
testCaseSegmentSet(cases, long.class, 1234, (vh, seg, off, tv) -> vh.set(seg, off, (int) tv));
testCaseSegmentSet(cases, long.class, (char) 1234, (vh, seg, off, tv) -> vh.set(seg, off, (char) tv));
testCaseSegmentSet(cases, long.class, (short) 1234, (vh, seg, off, tv) -> vh.set(seg, off, (short) tv));
testCaseSegmentSet(cases, long.class, (byte) 1234, (vh, seg, off, tv) -> vh.set(seg, off, (byte) tv));
testCaseSegmentSet(cases, double.class, 1234F, (vh, seg, off, tv) -> vh.set(seg, off, (float) tv));
return cases.toArray(Object[][]::new);
}

View File

@ -23,6 +23,7 @@
package org.openjdk.tests.java.util.stream;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemoryLayouts;
import jdk.incubator.foreign.MemorySegment;
@ -39,16 +40,8 @@ import org.testng.annotations.DataProvider;
public class SegmentTestDataProvider {
static VarHandle BYTE_HANDLE = MemoryLayouts.JAVA_BYTE.varHandle(byte.class);
static VarHandle CHAR_HANDLE = MemoryLayouts.JAVA_CHAR.withBitAlignment(8).varHandle(char.class);
static VarHandle SHORT_HANDLE = MemoryLayouts.JAVA_SHORT.withBitAlignment(8).varHandle(short.class);
static VarHandle INT_HANDLE = MemoryLayouts.JAVA_INT.withBitAlignment(8).varHandle(int.class);
static VarHandle LONG_HANDLE = MemoryLayouts.JAVA_LONG.withBitAlignment(8).varHandle(long.class);
static VarHandle FLOAT_HANDLE = MemoryLayouts.JAVA_FLOAT.withBitAlignment(8).varHandle(float.class);
static VarHandle DOUBLE_HANDLE = MemoryLayouts.JAVA_DOUBLE.withBitAlignment(8).varHandle(double.class);
static boolean compareSegmentsByte(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Byte> mapper = segment -> (byte)BYTE_HANDLE.get(segment.baseAddress());
Function<MemorySegment, Byte> mapper = MemoryAccess::getByte;
List<Byte> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
@ -59,7 +52,7 @@ public class SegmentTestDataProvider {
}
static boolean compareSegmentsChar(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Character> mapper = segment -> (char)CHAR_HANDLE.get(segment.baseAddress());
Function<MemorySegment, Character> mapper = MemoryAccess::getChar;
List<Character> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
@ -70,7 +63,7 @@ public class SegmentTestDataProvider {
}
static boolean compareSegmentsShort(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Short> mapper = segment -> (short)SHORT_HANDLE.get(segment.baseAddress());
Function<MemorySegment, Short> mapper = MemoryAccess::getShort;
List<Short> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
@ -81,7 +74,7 @@ public class SegmentTestDataProvider {
}
static boolean compareSegmentsInt(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Integer> mapper = segment -> (int)INT_HANDLE.get(segment.baseAddress());
Function<MemorySegment, Integer> mapper = MemoryAccess::getInt;
List<Integer> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
@ -92,7 +85,7 @@ public class SegmentTestDataProvider {
}
static boolean compareSegmentsLong(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Long> mapper = segment -> (long)LONG_HANDLE.get(segment.baseAddress());
Function<MemorySegment, Long> mapper = MemoryAccess::getLong;
List<Long> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
@ -103,7 +96,7 @@ public class SegmentTestDataProvider {
}
static boolean compareSegmentsFloat(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Float> mapper = segment -> (float)FLOAT_HANDLE.get(segment.baseAddress());
Function<MemorySegment, Float> mapper = MemoryAccess::getFloat;
List<Float> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
@ -122,7 +115,7 @@ public class SegmentTestDataProvider {
}
static boolean compareSegmentsDouble(Collection<MemorySegment> segments1, Collection<MemorySegment> segments2, boolean isOrdered) {
Function<MemorySegment, Double> mapper = segment -> (double)DOUBLE_HANDLE.get(segment.baseAddress());
Function<MemorySegment, Double> mapper = MemoryAccess::getDouble;
List<Double> list1 = segments1.stream()
.map(mapper)
.collect(Collectors.toList());
@ -134,7 +127,7 @@ public class SegmentTestDataProvider {
static void initSegment(MemorySegment segment) {
for (int i = 0 ; i < segment.byteSize() ; i++) {
BYTE_HANDLE.set(segment.baseAddress(), (byte)i);
MemoryAccess.setByte(segment, (byte)i);
}
}

View File

@ -66,7 +66,7 @@ public class SpliteratorTest {
public void testSegmentSpliterator(String name, SequenceLayout layout, SpliteratorTestHelper.ContentAsserter<MemorySegment> contentAsserter) {
try (MemorySegment segment = MemorySegment.allocateNative(layout)) {
SegmentTestDataProvider.initSegment(segment);
SpliteratorTestHelper.testSpliterator(() -> MemorySegment.spliterator(segment, layout),
SpliteratorTestHelper.testSpliterator(() -> segment.spliterator(layout),
SegmentTestDataProvider::segmentCopier, contentAsserter);
}
}

View File

@ -72,12 +72,12 @@ public class LoopOverConstant {
//setup native memory segment
static final MemoryAddress segment_addr = MemorySegment.allocateNative(ALLOC_SIZE).baseAddress();
static final MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE);
static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement());
static {
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment_addr, (long) i, i);
VH_int.set(segment, (long) i, i);
}
}
@ -100,7 +100,7 @@ public class LoopOverConstant {
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int segment_get() {
return (int)VH_int.get(segment_addr, 0L);
return (int)VH_int.get(segment, 0L);
}
@Benchmark
@ -122,7 +122,7 @@ public class LoopOverConstant {
public int segment_loop() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i++) {
res += (int) VH_int.get(segment_addr, (long)i);
res += (int) VH_int.get(segment, (long)i);
}
return res;
}

View File

@ -22,7 +22,6 @@
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import org.openjdk.jmh.annotations.Benchmark;
@ -74,7 +73,16 @@ public class LoopOverNew {
public void segment_loop() {
MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE);
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment.baseAddress(), (long) i, i);
VH_int.set(segment, (long) i, i);
}
segment.close();
}
@Benchmark
public void segment_loop_shared() {
MemorySegment segment = MemorySegment.allocateNative(ALLOC_SIZE).share();
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment, (long) i, i);
}
segment.close();
}

View File

@ -22,6 +22,7 @@
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
@ -73,7 +74,7 @@ public class LoopOverNonConstant {
}
segment = MemorySegment.allocateNative(ALLOC_SIZE);
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment.baseAddress(), (long) i, i);
VH_int.set(segment, (long) i, i);
}
byteBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.nativeOrder());
for (int i = 0; i < ELEM_SIZE; i++) {
@ -97,7 +98,7 @@ public class LoopOverNonConstant {
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int segment_get() {
return (int) VH_int.get(segment.baseAddress(), 0L);
return (int) VH_int.get(segment, 0L);
}
@Benchmark
@ -115,12 +116,20 @@ public class LoopOverNonConstant {
return res;
}
@Benchmark
public int segment_loop_static() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i ++) {
res += MemoryAccess.getIntAtIndex(segment, i);
}
return res;
}
@Benchmark
public int segment_loop() {
int sum = 0;
MemoryAddress base = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
sum += (int) VH_int.get(segment, (long) i);
}
return sum;
}
@ -128,7 +137,7 @@ public class LoopOverNonConstant {
@Benchmark
public int segment_loop_slice() {
int sum = 0;
MemoryAddress base = segment.asSlice(0, segment.byteSize()).baseAddress();
MemorySegment base = segment.asSlice(0, segment.byteSize());
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
@ -138,7 +147,7 @@ public class LoopOverNonConstant {
@Benchmark
public int segment_loop_readonly() {
int sum = 0;
MemoryAddress base = segment.withAccessModes(MemorySegment.READ).baseAddress();
MemorySegment base = segment.withAccessModes(MemorySegment.READ);
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}

View File

@ -22,6 +22,7 @@
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
@ -90,7 +91,7 @@ public class LoopOverNonConstantHeap {
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int segment_get() {
return (int) VH_int.get(segment.baseAddress(), 0L);
return (int) VH_int.get(segment, 0L);
}
@Benchmark
@ -111,17 +112,25 @@ public class LoopOverNonConstantHeap {
@Benchmark
public int segment_loop() {
int sum = 0;
MemoryAddress base = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
sum += (int) VH_int.get(segment, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_static() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i ++) {
res += MemoryAccess.getIntAtIndex(segment, i);
}
return res;
}
@Benchmark
public int segment_loop_slice() {
int sum = 0;
MemoryAddress base = segment.asSlice(0, segment.byteSize()).baseAddress();
MemorySegment base = segment.asSlice(0, segment.byteSize());
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
@ -131,7 +140,7 @@ public class LoopOverNonConstantHeap {
@Benchmark
public int segment_loop_readonly() {
int sum = 0;
MemoryAddress base = segment.withAccessModes(MemorySegment.READ).baseAddress();
MemorySegment base = segment.withAccessModes(MemorySegment.READ);
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}

View File

@ -22,7 +22,7 @@
*/
package org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import org.openjdk.jmh.annotations.Benchmark;
@ -95,8 +95,8 @@ public class LoopOverNonConstantMapped {
}
((MappedByteBuffer)byteBuffer).force();
}
segment = MemorySegment.mapFromPath(tempPath, 0L, ALLOC_SIZE, FileChannel.MapMode.READ_WRITE);
unsafe_addr = segment.baseAddress().toRawLongValue();
segment = MemorySegment.mapFile(tempPath, 0L, ALLOC_SIZE, FileChannel.MapMode.READ_WRITE);
unsafe_addr = segment.address().toRawLongValue();
}
@TearDown
@ -114,7 +114,7 @@ public class LoopOverNonConstantMapped {
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int segment_get() {
return (int) VH_int.get(segment.baseAddress(), 0L);
return (int) VH_int.get(segment, 0L);
}
@Benchmark
@ -135,17 +135,25 @@ public class LoopOverNonConstantMapped {
@Benchmark
public int segment_loop() {
int sum = 0;
MemoryAddress base = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
sum += (int) VH_int.get(segment, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_static() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i ++) {
res += MemoryAccess.getIntAtIndex(segment, i);
}
return res;
}
@Benchmark
public int segment_loop_slice() {
int sum = 0;
MemoryAddress base = segment.asSlice(0, segment.byteSize()).baseAddress();
MemorySegment base = segment.asSlice(0, segment.byteSize());
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
@ -155,7 +163,7 @@ public class LoopOverNonConstantMapped {
@Benchmark
public int segment_loop_readonly() {
int sum = 0;
MemoryAddress base = segment.withAccessModes(MemorySegment.READ).baseAddress();
MemorySegment base = segment.withAccessModes(MemorySegment.READ);
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}

View File

@ -0,0 +1,166 @@
/*
* 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.
*
* 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 org.openjdk.bench.jdk.incubator.foreign;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Warmup;
import sun.misc.Unsafe;
import java.lang.invoke.VarHandle;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.TimeUnit;
import static jdk.incubator.foreign.MemoryLayout.PathElement.sequenceElement;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@State(org.openjdk.jmh.annotations.Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(3)
public class LoopOverNonConstantShared {
static final Unsafe unsafe = Utils.unsafe;
static final int ELEM_SIZE = 1_000_000;
static final int CARRIER_SIZE = (int)JAVA_INT.byteSize();
static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE;
static final VarHandle VH_int = MemoryLayout.ofSequence(JAVA_INT).varHandle(int.class, sequenceElement());
MemorySegment segment;
long unsafe_addr;
ByteBuffer byteBuffer;
@Setup
public void setup() {
unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE);
for (int i = 0; i < ELEM_SIZE; i++) {
unsafe.putInt(unsafe_addr + (i * CARRIER_SIZE) , i);
}
segment = MemorySegment.allocateNative(ALLOC_SIZE).share();
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment, (long) i, i);
}
byteBuffer = ByteBuffer.allocateDirect(ALLOC_SIZE).order(ByteOrder.nativeOrder());
for (int i = 0; i < ELEM_SIZE; i++) {
byteBuffer.putInt(i * CARRIER_SIZE , i);
}
}
@TearDown
public void tearDown() {
segment.close();
unsafe.invokeCleaner(byteBuffer);
unsafe.freeMemory(unsafe_addr);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int unsafe_get() {
return unsafe.getInt(unsafe_addr);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int segment_get() {
return (int) VH_int.get(segment, 0L);
}
@Benchmark
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public int BB_get() {
return byteBuffer.getInt(0);
}
@Benchmark
public int unsafe_loop() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i ++) {
res += unsafe.getInt(unsafe_addr + (i * CARRIER_SIZE));
}
return res;
}
@Benchmark
public int segment_loop_static() {
int res = 0;
for (int i = 0; i < ELEM_SIZE; i ++) {
res += MemoryAccess.getIntAtIndex(segment, i);
}
return res;
}
@Benchmark
public int segment_loop() {
int sum = 0;
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(segment, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_slice() {
int sum = 0;
MemorySegment base = segment.asSlice(0, segment.byteSize());
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int segment_loop_readonly() {
int sum = 0;
MemorySegment base = segment.withAccessModes(MemorySegment.READ);
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int) VH_int.get(base, (long) i);
}
return sum;
}
@Benchmark
public int BB_loop() {
int sum = 0;
ByteBuffer bb = byteBuffer;
for (int i = 0; i < ELEM_SIZE; i++) {
sum += bb.getInt(i * CARRIER_SIZE);
}
return sum;
}
}

View File

@ -85,9 +85,9 @@ public class ParallelSum {
for (int i = 0; i < ELEM_SIZE; i++) {
unsafe.putInt(address + (i * CARRIER_SIZE), i);
}
segment = MemorySegment.allocateNative(ALLOC_SIZE);
segment = MemorySegment.allocateNative(ALLOC_SIZE).share();
for (int i = 0; i < ELEM_SIZE; i++) {
VH_int.set(segment.baseAddress(), (long) i, i);
VH_int.set(segment, (long) i, i);
}
}
@ -100,9 +100,8 @@ public class ParallelSum {
@Benchmark
public int segment_serial() {
int res = 0;
MemoryAddress base = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
res += (int)VH_int.get(base, (long) i);
res += (int)VH_int.get(segment, (long) i);
}
return res;
}
@ -118,73 +117,71 @@ public class ParallelSum {
@Benchmark
public int segment_parallel() {
return new SumSegment(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), SEGMENT_TO_INT).invoke();
return new SumSegment(segment.spliterator(SEQUENCE_LAYOUT), SEGMENT_TO_INT).invoke();
}
@Benchmark
public int segment_parallel_bulk() {
return new SumSegment(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), SEGMENT_TO_INT_BULK).invoke();
return new SumSegment(segment.spliterator(SEQUENCE_LAYOUT_BULK), SEGMENT_TO_INT_BULK).invoke();
}
@Benchmark
public int segment_stream_parallel() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true)
return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), true)
.mapToInt(SEGMENT_TO_INT).sum();
}
@Benchmark
public int segment_stream_parallel_bulk() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), true)
return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT_BULK), true)
.mapToInt(SEGMENT_TO_INT_BULK).sum();
}
final static ToIntFunction<MemorySegment> SEGMENT_TO_INT = slice ->
(int) VH_int.get(slice.baseAddress(), 0L);
(int) VH_int.get(slice, 0L);
final static ToIntFunction<MemorySegment> SEGMENT_TO_INT_BULK = slice -> {
int res = 0;
MemoryAddress base = slice.baseAddress();
for (int i = 0; i < BULK_FACTOR ; i++) {
res += (int)VH_int.get(base, (long) i);
res += (int)VH_int.get(slice, (long) i);
}
return res;
};
@Benchmark
public Optional<MemorySegment> segment_stream_findany_serial() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), false)
return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), false)
.filter(FIND_SINGLE)
.findAny();
}
@Benchmark
public Optional<MemorySegment> segment_stream_findany_parallel() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT), true)
return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT), true)
.filter(FIND_SINGLE)
.findAny();
}
@Benchmark
public Optional<MemorySegment> segment_stream_findany_serial_bulk() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), false)
return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT_BULK), false)
.filter(FIND_BULK)
.findAny();
}
@Benchmark
public Optional<MemorySegment> segment_stream_findany_parallel_bulk() {
return StreamSupport.stream(MemorySegment.spliterator(segment, SEQUENCE_LAYOUT_BULK), true)
return StreamSupport.stream(segment.spliterator(SEQUENCE_LAYOUT_BULK), true)
.filter(FIND_BULK)
.findAny();
}
final static Predicate<MemorySegment> FIND_SINGLE = slice ->
(int)VH_int.get(slice.baseAddress(), 0L) == (ELEM_SIZE - 1);
(int)VH_int.get(slice, 0L) == (ELEM_SIZE - 1);
final static Predicate<MemorySegment> FIND_BULK = slice -> {
MemoryAddress base = slice.baseAddress();
for (int i = 0; i < BULK_FACTOR ; i++) {
if ((int)VH_int.get(base, (long)i) == (ELEM_SIZE - 1)) {
if ((int)VH_int.get(slice, (long)i) == (ELEM_SIZE - 1)) {
return true;
}
}
@ -193,7 +190,7 @@ public class ParallelSum {
@Benchmark
public int unsafe_parallel() {
return new SumUnsafe(address, 0, ALLOC_SIZE).invoke();
return new SumUnsafe(address, 0, ALLOC_SIZE / CARRIER_SIZE).invoke();
}
static class SumUnsafe extends RecursiveTask<Integer> {
@ -212,15 +209,19 @@ public class ParallelSum {
@Override
protected Integer compute() {
if (length > SPLIT_THRESHOLD) {
SumUnsafe s1 = new SumUnsafe(address, start, length / 2);
SumUnsafe s2 = new SumUnsafe(address, length / 2, length / 2);
int rem = length % 2;
int split = length / 2;
int lobound = split;
int hibound = lobound + rem;
SumUnsafe s1 = new SumUnsafe(address, start, lobound);
SumUnsafe s2 = new SumUnsafe(address, start + lobound, hibound);
s1.fork();
s2.fork();
return s1.join() + s2.join();
} else {
int res = 0;
for (int i = 0; i < length; i += CARRIER_SIZE) {
res += unsafe.getInt(start + address + i);
for (int i = 0; i < length; i ++) {
res += unsafe.getInt(address + (start + i) * CARRIER_SIZE);
}
return res;
}

View File

@ -144,9 +144,8 @@ public class TestAdaptVarHandles {
@Benchmark
public int segment_loop() throws Throwable {
int sum = 0;
MemoryAddress baseAddress = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += (int)VH_addr_int.get(baseAddress, (long)i);
sum += (int)VH_addr_int.get(segment, (long)i);
}
return sum;
}
@ -154,9 +153,8 @@ public class TestAdaptVarHandles {
@Benchmark
public int segment_box_loop() throws Throwable {
int sum = 0;
MemoryAddress baseAddress = segment.baseAddress();
for (int i = 0; i < ELEM_SIZE; i++) {
sum += ((IntBox)VH_addr_box_int.get(baseAddress, (long)i)).intValue();
sum += ((IntBox)VH_addr_box_int.get(segment, (long)i)).intValue();
}
return sum;
}

View File

@ -53,7 +53,7 @@ public class VarHandleExact {
static final VarHandle generic;
static {
generic = MemoryHandles.withStride(MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder()), 4);
generic = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
exact = generic.withInvokeExactBehavior();
}
@ -71,16 +71,16 @@ public class VarHandleExact {
@Benchmark
public void exact_exactInvocation() {
exact.set(data.baseAddress(), (long) 0, 42);
exact.set(data, (long) 0, 42);
}
@Benchmark
public void generic_genericInvocation() {
generic.set(data.baseAddress(), 0, 42);
generic.set(data, 0, 42);
}
@Benchmark
public void generic_exactInvocation() {
generic.set(data.baseAddress(), (long) 0, 42);
generic.set(data, (long) 0, 42);
}
}

View File

@ -58,19 +58,19 @@ public class PanamaPoint implements AutoCloseable {
}
public void setX(int x) {
VH_x.set(segment.baseAddress(), x);
VH_x.set(segment, x);
}
public int getX() {
return (int) VH_x.get(segment.baseAddress());
return (int) VH_x.get(segment);
}
public void setY(int y) {
VH_y.set(segment.baseAddress(), y);
VH_y.set(segment, y);
}
public int getY() {
return (int) VH_y.get(segment.baseAddress());
return (int) VH_y.get(segment);
}
@Override