JNI, pthreads and ucontext in Ubuntu
Hi everyone,
I'm facing some trouble with JNI and ucontext under Linux Ubuntu 7.04. My JVM version is:
Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
Java HotSpot(TM) Client VM (build 14.2-b01, mixed mode, sharing)
I have a C program that successfully starts the JVM (in main method) using the following code, getting in "JavaVM *jvm" variable the JVM instance:
Code:
static JavaVM *jvm; // Reference to JVM
int main(void)
{
[INDENT]JNIEnv *env;
JavaVMOption *options;
JavaVMInitArgs vm_args;
JNIEnv *env;
jint result;
options=(void*)malloc(4*sizeof(JavaVMOption));
options[0].optionString="-Djava.class.path=.";
options[1].optionString="-Djava.compiler=NONE";
options[2].optionString="-verbose:gc,class,jni";
vm_args.version=JNI_VERSION_1_2;
vm_args.options=options;
vm_args.nOptions=3;
vm_args.ignoreUnrecognized=JNI_FALSE;
result=JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args);
// launch pthread, not shown here.[/INDENT]
}
After that, I launch a pthread in order to instantiate a precompiled java class named TestJNI using the already launched JVM. This class does nothing but printing "Hello!" inside it's default constructor. I instantiante pthread in main method this way:
Code:
pthread_t tid;
int thr_id;
// native_thread is main thread method
thr_id=pthread_create(&tid,NULL,native_thread,NULL);
In order to make JVM accessible inside a thread method, I must obtain the JNIEnv reference to JVM. This is done with AttachCurrentThread. After that, I instantiate successfully my TestJNI class. Code for "native_thread" method is:
Code:
void *native_thread(void *arg){
JNIEnv *env; // cannot be cached, each thread must attach to JVM and acquire it's own JNIEnv
// jvm has it's value set on first code snippet.
(*jvm)->AttachCurrentThread(jvm,(void**)&env,NULL);
// Get class reference (located on classpath ".")
cls=(*env)->FindClass(env,"TestJNI");
// Get reference to default constructor
mid=(*env)->GetMethodID(env,cls,"<init>","()V");
// Instantiate object (successfully)
jobj=(*env)->NewObject(env,cls,mid);
All code described above runs successfully.
However, my programs needs using <ucontext.h> capability of creating contexts to switch between them. Ucontext is used to implement, for example, coroutines. So my intention is to execute the same method "native_thread" after having switched the same thread to another context.
So, I have 2 ucontext_t variables.
Code:
ucontext_t new_coroutine;
ucontext_t thread_coroutine;
new_coroutine is the coroutine I switch to where I wan't to access JVM.
Initialization on "thread_coroutine" is done this way:
Code:
void prepare_context()
{
[INDENT]getcontext(&new_coroutine);
new_coroutine.uc_link = NULL;
// assign stack where coroutine can execute. 1MB stack.
new_coroutine.uc_stack.ss_sp = malloc(1024*1024);
new_coroutine.uc_stack.ss_size = (1024*1024);
new_coroutine.uc_stack.ss_flags = 0;
makecontext(&new_coroutine,(void *)my_coroutine_method,1,NULL);[/INDENT]
}
This way, my coroutine will execute "my_coroutine_method" which is a literal copy of already show "native_thread" method. This way, my "native_thread" method becomes:
Code:
void *native_thread(void *arg){
prepare_context();
// swap to new_coroutine to run "my_coroutine_method".
swapcontext(&(thread_coroutine), &(new_coroutine));
When coroutine reaches AttachCurrentThread, JVM generates a fatal error:
Code:
# Internal Error (threadLocalStorage.cpp:40), pid=4797, tid=3035433872
# Error: guarantee(get_thread() == thread,"must be the same thread, quickly")
threadLocalStorage.cpp::set_thread method is:
Code:
void ThreadLocalStorage::set_thread(Thread* thread) {
pd_set_thread(thread);
// The following ensure that any optimization tricks we have tried
// did not backfire on us:
// THIS IS WHERE EXCEPTION IS GENERATED!!!!
guarantee(get_thread() == thread, "must be the same thread, quickly");
guarantee(get_thread_slow() == thread, "must be the same thread, slowly");
}
This error makes me suspect that JVM tries to check that thread changed and exits. However, thread didn't change, only the stack where it executes, by means of ucontent_t.
I also tried executing AttachCurrentThread on main stack thread BEFORE switching to coroutine. In that case, AttachCurrentThread succeded inside coroutine. However, when i try to get reference to the class TestJNI (by means of FindClass method) i get a segmentation fault error:
Code:
siginfo:si_signo=SIGSEGV: si_errno=0, si_code=1 (SEGV_MAPERR), si_addr=0x00000094
Did anyone face this problem before?
Sorry for this mega-description of my situation, hope it isn't difficult to understand.
Many thanks in advance
Ignacio