8254162: Implementation of Foreign-Memory Access API (Third Incubator)
Reviewed-by: erikj, psandoz, alanb
This commit is contained in:
parent
c6ab0fdb15
commit
3e70aac5cc
@ -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.
|
||||
|
151
make/modules/java.base/gensrc/GensrcScopedMemoryAccess.gmk
Normal file
151
make/modules/java.base/gensrc/GensrcScopedMemoryAccess.gmk
Normal 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)
|
@ -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))
|
||||
|
@ -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) */ \
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
186
src/hotspot/share/prims/scopedMemoryAccess.cpp
Normal file
186
src/hotspot/share/prims/scopedMemoryAccess.cpp
Normal 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(®ister_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, ®ister_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
|
35
src/hotspot/share/prims/scopedMemoryAccess.hpp
Normal file
35
src/hotspot/share/prims/scopedMemoryAccess.hpp
Normal 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
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
############################################################################
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
185
test/jdk/java/foreign/TestCleaner.java
Normal file
185
test/jdk/java/foreign/TestCleaner.java
Normal 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);
|
||||
}
|
||||
}
|
260
test/jdk/java/foreign/TestHandshake.java
Normal file
260
test/jdk/java/foreign/TestHandshake.java
Normal 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 }
|
||||
};
|
||||
}
|
||||
}
|
@ -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() };
|
||||
|
@ -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));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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[][]{
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 } },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user