¿Cómo usar "inseguro" en JNI?
Digamos que tengo un valor señalado por una tupla (base, offset).
p.ej.
class Data{
int x = 0;
}
class Accessor{
public Data data;
public Object base$x;
public long off$x;
public static final Unsafe unsafe;
public void run(){
data = new Data();
base$x = data;
off$x = 12;
unsafe.putInt(base$x,off$x,1);
assert(data.x == 1);
}
static{
try {
Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor();
unsafeConstructor.setAccessible(true);
unsafe = unsafeConstructor.newInstance();
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
throw new RuntimeException(e);
}
}
}
(versión ejecutable:https://ideone.com/OasQrh )
Ahora quiero ejecutar
unsafe.setInt(base$x,off$x,1);
enC
.
¿Por qué? Porque un proceso diferente puede mover los datos y cambiarbase
yoff
para apuntar a la nueva ubicación. Quiero usar Intel RTM para asegurarme de que mover los datos no interfiera con los accesos regulares al campo.
Así que creemos una nueva clase
class Transactionally{
static{
System.loadLibrary("RTM_transact");
}
public static native void setInt(Object target, String fieldname);
}
y reemplazar
class Accessor{
public Data data;
public Object base;
public long off;
public static final Unsafe unsafe;
public void run(){
data = new Data();
base$x = data;
off$x = 12;
Transactionally.setInt(this,"x",1);
assert(data.x == 1);
}
static{
try {
Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor();
unsafeConstructor.setAccessible(true);
unsafe = unsafeConstructor.newInstance();
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
throw new RuntimeException(e);
}
}
}
ejecutamos esto a través de javah para obtener el encabezado, copiamos las firmas del método en unRTM_transct.cpp
y obtener algo como
#include<jni.h>
#include "rtmbenchmark_Transactionally.h"
#include <string>
std::string fldBase("base$");
std::string fldOff("off$");
/*
* Class: rtmbenchmark_Transactionally
* Method: setInt
* Signature: (Ljava/lang/Object;Ljava/lang/String;I)V
*/
JNIEXPORT void JNICALL Java_rtmbenchmark_Transactionally_setInt(JNIEnv *env, jclass _obsolete, jobject target, jstring fieldname, jint value){
jclass targetClass = (*env)->GetObjClass(env,target);
jfieldID fidFLDbase = (*env)->GetFieldID(env, targetClass, fldBase + fieldname, "Ljava.lang.Object");
jfieldid fidFLDoff = (*env)->GetFieldID(env, targetClass, fldOff + fieldname, "J");
volatile int tries = 0;
volatile boolean success = 0;
while(!success){/*TODO: maybe switch to a different strategy when tries grows too large?*/
__asm__ __volatile__ (
"xbegin 1f" /*1f: local label 1, look forward to find first*/
:"+rm"(tries) /*artificial dependency to prevent re-ordering*/
);
++tries;
jobject base = (*env)->GetIntField(env,targetClass,fidFLDbase);
jlong offset = (*env)->GetLongField(env,targetClass,fidFLDoff);
//??? ==> unsafe.setLong(base,offset,value)
__asm__ __volatile__ (
"xend\n\t"
"incl %0\n" /*increment success ==> break out of loop*/
"jmp 2f\n" /*jump to end of loop*/
"1:\n\t" /*local label 1 (jumped to when transaction is aborted)*/
"2:" /*local label 2 (jumped to after transaction succeeded)*/
:"+rm"(success)
:"rm"(tries) /*artificial dependency*/
);
}
}
¿Qué uso para inseguro, en el cuerpo de la transacción?
El "corazón" de la transacción son estas tres líneas que (se supone que) corresponden a lo que solía hacer Accessor:
jobject base = (*env)->GetIntField(env,targetClass,fidFLDbase);
jlong offset = (*env)->GetLongField(env,targetClass,fidFLDoff);
//??? ==> unsafe.setInt(base,offset,value)
)
Idealmente, por supuesto, me gustaría hacer algo parecido abase[offset] = value
, pero dudo mucho que funcione.
¿Qué pongo aquí para unsafe.setInt?