/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This source file is part of SableVM. * * * * See the file "LICENSE" for the copyright information and for * * the terms and conditions for copying, distribution and * * modification of this source file. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Thread specific data */ static _svmt_thread_key _svmv_env_key; /* key to access thread specific environment data */ /* stack_offset */ static const size_t _svmv_stack_offset = (sizeof (_svmt_stack_frame) + (SVM_ALIGNMENT - 1)) & ~((size_t) (SVM_ALIGNMENT - 1)); /* ---------------------------------------------------------------------- _svmf_thread_init ---------------------------------------------------------------------- */ svm_static jint _svmf_thread_init (void) { /* create key for thread specific data */ if (_svmm_thread_key_create (_svmv_env_key) != JNI_OK) { goto error; } return JNI_OK; error: return JNI_ERR; } /* ---------------------------------------------------------------------- _svmf_get_current_env ---------------------------------------------------------------------- */ _svmt_JNIEnv * _svmf_get_current_env (void) { return (_svmt_JNIEnv *) _svmm_thread_getspecific (_svmv_env_key); } /* ---------------------------------------------------------------------- _svmf_set_current_env ---------------------------------------------------------------------- */ static void _svmf_set_current_env (_svmt_JNIEnv *env) { _svmm_thread_setspecific (_svmv_env_key, env); } /* ---------------------------------------------------------------------- _svmf_halt_if_requested ---------------------------------------------------------------------- */ /* IMPORTANT: The calling thread should hold the lock on vm->global_mutex when calling this function. */ svm_static void _svmf_halt_if_requested (_svmt_JNIEnv *env) { _svmt_JavaVM *vm = env->vm; assert (env->thread_status != SVM_THREAD_STATUS_HALTED); assert (env->thread_status != SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED); assert (env->thread_status != SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED); /* is halting requested by another thread? */ while (env->thread_status == SVM_THREAD_STATUS_HALT_REQUESTED) { env->thread_status = SVM_THREAD_STATUS_HALTED; /* there must be a reason for halting */ assert (env->status_flags != SVM_THREAD_STATUS_FLAGS_NONE); /* are we halting for (among other things) stopping the world? */ if (_svmf_is_set_flag (env->status_flags, SVM_THREAD_STATUS_FLAGS_STOP_THE_WORLD)) { assert (vm->stop_the_world.pending_halt_thread_count > 0); vm->stop_the_world.pending_halt_thread_count--; /* is there any other thread pending halt? */ if (vm->stop_the_world.pending_halt_thread_count == 0) { /* no, so signal requesting thread */ _svmm_cond_signal (vm->stop_the_world.requesting_thread_cond); } } /* are we halting for (among other things) suspending this thread? */ if (_svmf_is_set_flag (env->status_flags, SVM_THREAD_STATUS_FLAGS_SUSPEND)) { _svmt_JNIEnv *current; /* signal all threads that requested the suspension of this thread. suspender_list could be NULL if the thread has suspended itself and is the only that call suspend_thread. */ current = env->suspension.suspender_list; while (current != NULL) { _svmm_cond_signal (current->suspension.cond); current = current->suspension.suspender_list_next; } } /* time to halt the thread */ do { _svmm_cond_wait (env->wakeup_cond, vm->global_mutex); } while (env->thread_status == SVM_THREAD_STATUS_HALTED); } assert (env->thread_status == SVM_THREAD_STATUS_RUNNING_JAVA); } /* ---------------------------------------------------------------------- _svmf_periodic_check ---------------------------------------------------------------------- */ inline svm_static void _svmf_periodic_check (_svmt_JNIEnv *env) { #ifndef NDEBUG jint status = env->thread_status; assert (status == SVM_THREAD_STATUS_RUNNING_JAVA || status == SVM_THREAD_STATUS_HALT_REQUESTED); #endif /* is stopping the world requested by another thread? */ if (env->thread_status == SVM_THREAD_STATUS_HALT_REQUESTED) { _svmt_JavaVM *vm = env->vm; _svmm_mutex_lock (vm->global_mutex); _svmf_halt_if_requested (env); _svmm_mutex_unlock (); } } /* --------------------------------------------------------------------- _svmf_is_suspended --------------------------------------------------------------------- */ /* IMPORTANT: The calling thread should hold the lock on vm->global_mutex when calling this function. */ static jboolean _svmf_is_suspended (_svmt_JNIEnv *target) { jboolean result = JNI_FALSE; if (((target->thread_status == SVM_THREAD_STATUS_HALTED) || (target->thread_status == SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED)) && _svmf_is_set_flag (target->status_flags, SVM_THREAD_STATUS_FLAGS_SUSPEND)) { result = JNI_TRUE; } return result; } /* --------------------------------------------------------------------- _svmf_suspend_thread --------------------------------------------------------------------- */ /* IMPORTANT: The calling thread should hold the lock on vm->global_mutex when calling this function. Once the thread is halted, the calling thread should release the lock until just prior the call to resume_thread. returns: JNI_FALSE: the thread was already suspended JNI_TRUE: the thread has been suspended by this call */ static jboolean _svmf_suspend_thread (_svmt_JNIEnv *env, _svmt_JNIEnv *target) { jboolean result, succeeded; _svmt_JavaVM *vm = env->vm; /* halt if requested */ _svmf_halt_if_requested (env); /* suspend the calling thread */ if (target == env) { assert (target->thread_status == SVM_THREAD_STATUS_RUNNING_JAVA); target->thread_status = SVM_THREAD_STATUS_HALTED; _svmm_set_flag (target->status_flags, SVM_THREAD_STATUS_FLAGS_SUSPEND); assert (env->suspension.suspender_list_next == NULL); /* halt the thread */ do { _svmm_cond_wait (env->wakeup_cond, vm->global_mutex); } while (_svmf_is_suspended (target)); } /* suspend another thread */ else { do { switch (target->thread_status) { case SVM_THREAD_STATUS_RUNNING_JAVA: { succeeded = _svmm_compare_and_swap (target->thread_status, SVM_THREAD_STATUS_RUNNING_JAVA, SVM_THREAD_STATUS_HALT_REQUESTED); } break; case SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED: { succeeded = _svmm_compare_and_swap (target->thread_status, SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED, SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED); } break; case SVM_THREAD_STATUS_HALT_REQUESTED: case SVM_THREAD_STATUS_HALTED: case SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED: { succeeded = JNI_TRUE; } break; default: { succeeded = JNI_FALSE; /* to keep compiler happy */ _svmm_fatal_error ("impossible control flow"); } break; } } while (!succeeded); if (_svmf_is_suspended (target)) { /* The thread was already halted for suspension */ result = JNI_FALSE; goto end; } _svmm_set_flag (target->status_flags, SVM_THREAD_STATUS_FLAGS_SUSPEND); if (target->thread_status == SVM_THREAD_STATUS_HALTED || target->thread_status == SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED) { /* The thread was already halted for some reason or not running java */ result = JNI_TRUE; goto end; } assert (target->thread_status == SVM_THREAD_STATUS_HALT_REQUESTED); assert (env->suspension.suspender_list_next == NULL); env->suspension.suspender_list_next = target->suspension.suspender_list; target->suspension.suspender_list = env; /* wait until target is halted */ do { _svmm_cond_wait (env->suspension.cond, vm->global_mutex); } while (target->thread_status == SVM_THREAD_STATUS_HALT_REQUESTED); /* once the target thread is halted (or it has been resumed by another thread) */ { _svmt_JNIEnv *previous = NULL; _svmt_JNIEnv *current = target->suspension.suspender_list; /* remove this thread from the suspender_list */ while (current != env) { previous = current; current = current->suspension.suspender_list_next; assert (current != NULL); } assert (current == env); if (previous != NULL) { previous->suspension.suspender_list_next = env->suspension.suspender_list_next; } else { target->suspension.suspender_list = env->suspension.suspender_list_next; } env->suspension.suspender_list_next = NULL; } } result = JNI_TRUE; end: return result; } /* --------------------------------------------------------------------- _svmf_resume_thread --------------------------------------------------------------------- */ /* IMPORTANT: The calling thread should hold the lock on vm->global_mutex when calling this function. returns: JNI_FALSE: the thread was already resumed JNI_TRUE: the thread has been resumed by this call */ static jboolean _svmf_resume_thread (_svmt_JNIEnv *env, _svmt_JNIEnv *target) { jboolean result; /* halt if requested */ _svmf_halt_if_requested (env); assert (env->thread_status == SVM_THREAD_STATUS_RUNNING_JAVA); if (target == env) { /* if the current thread calls this function on itself, it cant be sleeping */ assert (!_svmf_is_set_flag (target->status_flags, SVM_THREAD_STATUS_FLAGS_SUSPEND)); result = JNI_FALSE; goto end; } if (!_svmf_is_set_flag (target->status_flags, SVM_THREAD_STATUS_FLAGS_SUSPEND)) { /* target thread is not suspended */ result = JNI_FALSE; goto end; } /* unsuspend target thread */ _svmm_clear_flag (target->status_flags, SVM_THREAD_STATUS_FLAGS_SUSPEND); if (target->thread_status == SVM_THREAD_STATUS_HALT_REQUESTED) { _svmt_JNIEnv *current; /* signal all threads that have requested the suspension of the target thread. */ current = target->suspension.suspender_list; while (current != NULL) { _svmm_cond_signal (current->suspension.cond); current = current->suspension.suspender_list_next; } } if (target->status_flags != SVM_THREAD_STATUS_FLAGS_NONE) { /* target must remain halted for some other reason */ result = JNI_TRUE; goto end; } switch (target->thread_status) { case SVM_THREAD_STATUS_HALTED: { target->thread_status = SVM_THREAD_STATUS_RUNNING_JAVA; _svmm_cond_signal (target->wakeup_cond); } break; case SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED: { target->thread_status = SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED; _svmm_cond_signal (target->wakeup_cond); } break; case SVM_THREAD_STATUS_HALT_REQUESTED: { target->thread_status = SVM_THREAD_STATUS_RUNNING_JAVA; } break; case SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED: case SVM_THREAD_STATUS_RUNNING_JAVA: default: { _svmm_fatal_error ("impossible control flow"); } break; } result = JNI_TRUE; end: return result; } /* ---------------------------------------------------------------------- _svmf_stop_the_world ---------------------------------------------------------------------- */ /* IMPORTANT: The calling thread should hold the lock on vm->global_mutex when calling this function. Once the world is stopped, the calling thread should release the lock until just prior the call to resume_the_world. */ #ifndef _SABLEVM_NO_GC svm_static void _svmf_stop_the_world (_svmt_JNIEnv *env) { _svmt_JavaVM *vm = env->vm; jint i; /* if another thread beat us to it, halt */ _svmf_halt_if_requested (env); /* ok, now we can proceed */ assert (env->thread_status == SVM_THREAD_STATUS_RUNNING_JAVA); assert (vm->stop_the_world.pending_halt_thread_count == 0); for (i = 0; i < 2; i++) { _svmt_JNIEnv *current; /* visit all threads */ for (current = ((i == 0) ? vm->threads.user : vm->threads.system); current != NULL; current = current->next) { jboolean succeeded; /* skip the running thread */ if (current == env) { continue; } /* request halt */ do { switch (current->thread_status) { case SVM_THREAD_STATUS_RUNNING_JAVA: { succeeded = _svmm_compare_and_swap (current->thread_status, SVM_THREAD_STATUS_RUNNING_JAVA, SVM_THREAD_STATUS_HALT_REQUESTED); if (succeeded) { vm->stop_the_world.pending_halt_thread_count++; } } break; case SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED: { succeeded = _svmm_compare_and_swap (current->thread_status, SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED, SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED); } break; case SVM_THREAD_STATUS_HALT_REQUESTED: case SVM_THREAD_STATUS_HALTED: case SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED: { succeeded = JNI_TRUE; } break; default: { succeeded = JNI_FALSE; /* to keep compiler happy */ _svmm_fatal_error ("impossible control flow"); } break; } } while (!succeeded); _svmm_set_flag (current->status_flags, SVM_THREAD_STATUS_FLAGS_STOP_THE_WORLD); } } /* wait for other threads to halt */ while (vm->stop_the_world.pending_halt_thread_count != 0) { _svmm_cond_wait (vm->stop_the_world.requesting_thread_cond, vm->global_mutex); } /* the world is stopped! we can resume... */ return; } #endif /* !_SABLEVM_NO_GC */ /* ---------------------------------------------------------------------- _svmf_resume_the_world ---------------------------------------------------------------------- */ /* IMPORTANT: The calling thread should acquire the lock on vm->global_mutex before calling this function. Of course, this function should not be invoked unless the world has been already stopped by the calling thread. */ #ifndef _SABLEVM_NO_GC svm_static void _svmf_resume_the_world (_svmt_JNIEnv *env) { _svmt_JavaVM *vm = env->vm; jint i; assert (env->thread_status == SVM_THREAD_STATUS_RUNNING_JAVA); assert (vm->stop_the_world.pending_halt_thread_count == 0); for (i = 0; i < 2; i++) { _svmt_JNIEnv *current; /* visit all threads */ for (current = ((i == 0) ? vm->threads.user : vm->threads.system); current != NULL; current = current->next) { /* skip the running thread */ if (current == env) { continue; } assert (_svmf_is_set_flag (current->status_flags, SVM_THREAD_STATUS_FLAGS_STOP_THE_WORLD)); /* clear stop the world status */ _svmm_clear_flag (current->status_flags, SVM_THREAD_STATUS_FLAGS_STOP_THE_WORLD); if (current->status_flags == SVM_THREAD_STATUS_FLAGS_NONE) { switch (current->thread_status) { case SVM_THREAD_STATUS_HALTED: { current->thread_status = SVM_THREAD_STATUS_RUNNING_JAVA; _svmm_cond_signal (current->wakeup_cond); } break; case SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED: { current->thread_status = SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED; _svmm_cond_signal (current->wakeup_cond); } break; case SVM_THREAD_STATUS_HALT_REQUESTED: case SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED: case SVM_THREAD_STATUS_RUNNING_JAVA: default: { _svmm_fatal_error ("impossible control flow"); } break; } } } } /* the world will resume execution as soon as this thread releases the global mutex... */ return; } #endif /* !_SABLEVM_NO_GC */ /* ---------------------------------------------------------------------- _svmf_inflate_lock_no_exception ---------------------------------------------------------------------- */ svm_static jint _svmf_inflate_lock_no_exception (_svmt_JNIEnv *env, _svmt_object_instance *instance) { _svmt_JavaVM *vm = env->vm; jint status = JNI_OK; _svmt_fat_lock *fat_lock; _svmm_mutex_lock (vm->global_mutex); fat_lock = vm->fat_locks.free_list; if (fat_lock != NULL) { assert (fat_lock->recursive_count == 0); assert (fat_lock->owner == NULL); vm->fat_locks.free_list = fat_lock->next; fat_lock->next = NULL; } else { if (vm->fat_locks.next_fat_lock_id > SVM_MAX_FATLOCK_ID) { status = JNI_ERR; } else { if (_svmm_gzalloc_fat_lock_no_exception (fat_lock) != JNI_OK) { status = JNI_ERR; } else { fat_lock->id = vm->fat_locks.next_fat_lock_id++; vm->fat_locks.array[fat_lock->id] = fat_lock; _svmm_mutex_init (fat_lock->mutex); _svmm_cond_init (fat_lock->cond); _svmm_cond_init (fat_lock->notification_cond); } } } _svmm_mutex_unlock (); if (status != JNI_OK) { return JNI_ERR; } _svmm_mutex_lock (fat_lock->mutex); assert (fat_lock->recursive_count == 0); assert (fat_lock->owner == NULL); fat_lock->owner = env; /* Note that we are NOT incrementing the recursive count in the following code. A thin lock recursive count of 0 is equivalent to a fat lock recursive count of 1. */ fat_lock->recursive_count = _svmf_lockword_get_thinlock_recursive_count (instance->lockword) + 1; instance->lockword = _svmf_lockword_fatlock (fat_lock->id, _svmf_lockword_get_extra_bits (instance->lockword)); _svmm_mutex_unlock (); return JNI_OK; } /* ---------------------------------------------------------------------- _svmm_stopping_java ---------------------------------------------------------------------- */ #ifndef NDEBUG #if defined(__func__) #define _svmm_stopping_java(env) \ _svmm_stopping_java_aux(env, __FILE__, __func__, __LINE__) #elif defined(__FUNCTION__) #define _svmm_stopping_java(env) \ _svmm_stopping_java_aux(env, __FILE__, __FUNCTION__, __LINE__) #else #define _svmm_stopping_java(env) \ _svmm_stopping_java_aux(env, __FILE__, "unknown", __LINE__) #endif #define _svmm_stopping_java_aux(env, file_, func_, line_) \ do \ { \ jint last_index = (env->stop_resume.last_index + 1) % SVM_STOP_RESUME_TRACE_SIZE; \ env->stop_resume.last_index = last_index; \ env->stop_resume.info[last_index].action = "stop"; \ env->stop_resume.info[last_index].file = file_; \ env->stop_resume.info[last_index].func = func_; \ env->stop_resume.info[last_index].line = line_; \ env->stop_resume.info[last_index].entry = env->thread_status; \ env->stop_resume.info[last_index].exit = -1; \ _svmh_stopping_java(env); \ env->stop_resume.info[last_index].exit = env->thread_status; \ } while(JNI_FALSE) #else #define _svmm_stopping_java(env) \ _svmh_stopping_java(env) #endif svm_static void _svmh_stopping_java (_svmt_JNIEnv *env) { _svmt_JavaVM *vm = env->vm; #ifndef NDEBUG jint status = env->thread_status; if (status != SVM_THREAD_STATUS_RUNNING_JAVA && status != SVM_THREAD_STATUS_HALT_REQUESTED) { jint last_index = env->stop_resume.last_index; jint current = last_index; do { current = (current + 1) % SVM_STOP_RESUME_TRACE_SIZE; if (env->stop_resume.info[current].action != NULL) { _svmf_printf (env, stderr, "%s %s %s %d %d %d\n", env->stop_resume.info[current].action, env->stop_resume.info[current].file, env->stop_resume.info[current].func, env->stop_resume.info[current].line, env->stop_resume.info[current].entry, env->stop_resume.info[current].exit); fflush (NULL); } } while (current != last_index); } assert (status == SVM_THREAD_STATUS_RUNNING_JAVA || status == SVM_THREAD_STATUS_HALT_REQUESTED); #endif if (!_svmm_compare_and_swap (env->thread_status, SVM_THREAD_STATUS_RUNNING_JAVA, SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED)) { _svmm_mutex_lock (vm->global_mutex); _svmf_halt_if_requested (env); assert (env->thread_status == SVM_THREAD_STATUS_RUNNING_JAVA); env->thread_status = SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED; _svmm_mutex_unlock (); } } /* ---------------------------------------------------------------------- _svmm_resuming_java ---------------------------------------------------------------------- */ #ifndef NDEBUG #if defined(__func__) #define _svmm_resuming_java(env) \ _svmm_resuming_java_aux(env, __FILE__, __func__, __LINE__) #elif defined(__FUNCTION__) #define _svmm_resuming_java(env) \ _svmm_resuming_java_aux(env, __FILE__, __FUNCTION__, __LINE__) #else #define _svmm_resuming_java(env) \ _svmm_resuming_java_aux(env, __FILE__, "unknown", __LINE__) #endif #define _svmm_resuming_java_aux(env, file_, func_, line_) \ do \ { \ jint last_index = (env->stop_resume.last_index + 1) % SVM_STOP_RESUME_TRACE_SIZE; \ env->stop_resume.last_index = last_index; \ env->stop_resume.info[last_index].action = "resume"; \ env->stop_resume.info[last_index].file = file_; \ env->stop_resume.info[last_index].func = func_; \ env->stop_resume.info[last_index].line = line_; \ env->stop_resume.info[last_index].entry = env->thread_status; \ env->stop_resume.info[last_index].exit = -1; \ _svmh_resuming_java(env); \ env->stop_resume.info[last_index].exit = env->thread_status; \ } while(JNI_FALSE) #else #define _svmm_resuming_java(env) \ _svmh_resuming_java(env) #endif svm_static void _svmh_resuming_java (_svmt_JNIEnv *env) { _svmt_JavaVM *vm = env->vm; #ifndef NDEBUG jint status = env->thread_status; if (status != SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED && status != SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED) { jint last_index = env->stop_resume.last_index; jint current = last_index; do { current = (current + 1) % SVM_STOP_RESUME_TRACE_SIZE; if (env->stop_resume.info[current].action != NULL) { _svmf_printf (env, stderr, "%s %s %s %d %d %d\n", env->stop_resume.info[current].action, env->stop_resume.info[current].file, env->stop_resume.info[current].func, env->stop_resume.info[current].line, env->stop_resume.info[current].entry, env->stop_resume.info[current].exit); fflush (NULL); } } while (current != last_index); } assert (status == SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED || status == SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED); #endif if (!_svmm_compare_and_swap (env->thread_status, SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED, SVM_THREAD_STATUS_RUNNING_JAVA)) { _svmm_mutex_lock (vm->global_mutex); while (env->thread_status == SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED) { _svmm_cond_wait (env->wakeup_cond, vm->global_mutex); } assert (env->thread_status == SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED); env->thread_status = SVM_THREAD_STATUS_RUNNING_JAVA; _svmm_mutex_unlock (); } } /* ---------------------------------------------------------------------- _svmf_enter_object_monitor ---------------------------------------------------------------------- */ inline svm_static jint _svmf_enter_object_monitor (_svmt_JNIEnv *env, _svmt_object_instance *instance) { _svmt_JavaVM *vm = env->vm; _svmt_word old_lockword; #ifndef NDEBUG const char *type_name = instance->vtable->type->name; #endif assert (instance != NULL); retry: old_lockword = instance->lockword; assert (env->thread_status != SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED); assert (env->thread_status != SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED); { _svmt_word new_lockword = env->vm_thread.thinlock_id | _svmf_lockword_get_extra_bits (old_lockword); if (_svmm_compare_and_swap (instance->lockword, _svmf_lockword_get_extra_bits (old_lockword), new_lockword)) { /* thin lock acquired */ return JNI_OK; } } /* Either the object is already locked, or the lock is inflated. */ if (_svmf_lockword_is_thin (old_lockword)) { /* it is a thin lock */ if (_svmf_lockword_get_thinlock_id (old_lockword) == env->vm_thread.thinlock_id) { /* the thinlock is already owned by the current thread */ jint recursive_count = _svmf_lockword_get_thinlock_recursive_count (old_lockword); assert (recursive_count <= SVM_THINLOCK_MAX_RECURSIVE_COUNT); if (++recursive_count <= SVM_THINLOCK_MAX_RECURSIVE_COUNT) { instance->lockword = _svmf_lockword_thinlock (env->vm_thread.thinlock_id, recursive_count, _svmf_lockword_get_extra_bits (old_lockword)); /* thin lock re-entered */ return JNI_OK; } /* Overflow! We must inflate the lock. */ if (_svmf_inflate_lock_no_exception (env, instance) != JNI_OK) { _svmf_error_OutOfMemoryError (env); return JNI_ERR; } /* OK, retry locking with the new fat lock */ goto retry; } else { /* contention: the thinlock is owned by another thread */ jint owner_id = _svmf_lockword_get_thread_id (old_lockword); _svmt_JNIEnv *owner = vm->threads.array[owner_id]; _svmt_word old_flag; jboolean notified; _svmt_word refetched_lockword; /* If we read "old_lockword" before the thinlock was actually acquired by another thread, retry. */ if (owner == NULL) { goto retry; } /* notify owning thread that inflation is requested */ _svmm_mutex_lock (owner->contention.owner.mutex); old_flag = owner->contention.owner.flag; owner->contention.owner.flag = 1; /* re-fetch the instance's lockword, now that the thread has been notified */ refetched_lockword = instance->lockword; if (_svmf_lockword_is_thin (refetched_lockword) && _svmf_lockword_get_thinlock_id (refetched_lockword) == owner->vm_thread.thinlock_id) { /* the thinlock owner is still the same and has been notified */ notified = JNI_TRUE; /* add this thread into the lock owner's wait_list */ env->contention.requester.wait_list_next = owner->contention.owner.wait_list; owner->contention.owner.wait_list = env; *(env->contention.requester.jobject) = instance; } else { /* the lock is not thin anymore or the owner has changed, so restore the contention flag to its original value */ notified = JNI_FALSE; owner->contention.owner.flag = old_flag; } _svmm_mutex_unlock (); /* if the owner has changed, retry from scratch */ if (!notified) { /* retry locking */ goto retry; } /* This one is tricky! To avoid deadlocks, transition to stopping java must be done while the owner's contention mutex is released. */ _svmm_stopping_java (env); /* IMPORTANT: From now on, we MUST NOT read or write any value which can be changed by GC or any other "stop the word" dependent operation. */ /* transition made; reacquire lock */ _svmm_mutex_lock (owner->contention.owner.mutex); /* while thread is still on the owner's wait list, then wait */ #ifndef NDEBUG if (vm->verbose_synchronization) { /* dump a stack trace on contention */ _svmf_printf (env, stderr, "*** CONTENTION ***\n"); _svmf_printf (env, stderr, "Thread (%d,%d) already owns the thin monitor of type %s seeked by thread (%d,%d)\n", owner->vm_thread.id, owner->vm_thread.thread, type_name, env->vm_thread.id, env->vm_thread.thread); _svmf_dump_stack_trace (env); } #endif do { _svmt_JNIEnv *current = owner->contention.owner.wait_list; while (current != NULL && current != env) { current = current->contention.requester.wait_list_next; } if (current == NULL) { break; } assert (current == env); assert (owner->contention.owner.flag == 1); _svmm_cond_wait (env->contention.requester.cond, owner->contention.owner.mutex); } while (JNI_TRUE); #ifndef NDEBUG if (vm->verbose_synchronization) { /* tell about resolved contention */ _svmf_printf (env, stderr, "*** RESOLVED CONTENTION ***\n"); _svmf_printf (env, stderr, "Thread (%d,%d) has finally got hold of the monitor.\n", env->vm_thread.id, env->vm_thread.thread); } #endif _svmm_mutex_unlock (); _svmm_resuming_java (env); /* GC could have moved things around... */ instance = *(env->contention.requester.jobject); *(env->contention.requester.jobject) = NULL; /* retry locking */ goto retry; } } else { /* it is a fat lock */ _svmt_word fat_index = _svmf_lockword_get_fatlock_index (old_lockword); _svmt_fat_lock *fat_lock = vm->fat_locks.array[fat_index]; jint status = JNI_OK; jobject obj; if (_svmm_new_native_local (env, obj) != JNI_OK) { return JNI_ERR; } /* save reference in case GC happens */ *obj = instance; _svmm_stopping_java (env); /* IMPORTANT: From now on, we MUST NOT read or write any value which can be changed by GC or any other "stop the word" dependent operation. */ _svmm_mutex_lock (fat_lock->mutex); #ifndef NDEBUG /* dump a stack trace on contention */ { jboolean contention_happened = JNI_FALSE; if (vm->verbose_synchronization) { if (fat_lock->recursive_count != 0 && fat_lock->owner != env) { contention_happened = JNI_TRUE; _svmf_printf (env, stderr, "*** CONTENTION ***\n"); _svmf_printf (env, stderr, "Thread (%d,%d) already owns the fat monitor %p of type %s seeked by thread (%d,%d)\n", fat_lock->owner->vm_thread.id, fat_lock->owner->vm_thread.thread, fat_lock, type_name, env->vm_thread.id, env->vm_thread.thread); _svmf_dump_stack_trace (env); } } #endif /* wait until no other thread owns the lock */ while (fat_lock->recursive_count != 0 && fat_lock->owner != env) { _svmm_cond_wait (fat_lock->cond, fat_lock->mutex); } #ifndef NDEBUG if (vm->verbose_synchronization) { /* tell about resolved contention */ if (contention_happened) { _svmf_printf (env, stderr, "*** RESOLVED CONTENTION ***\n"); _svmf_printf (env, stderr, "Thread (%d,%d) has finally got hold of the monitor.\n", env->vm_thread.id, env->vm_thread.thread); } } } #endif if (fat_lock->recursive_count == 0) { fat_lock->recursive_count = 1; fat_lock->owner = env; #ifndef NDEBUG if (vm->verbose_synchronization) { /* dump a stack trace on fat lock acquisition */ _svmf_printf (env, stderr, "*** FAT LOCK ACQUIRED ***\n"); _svmf_printf (env, stderr, "Thread (%d,%d) aquires monitor %p of type %s\n", env->vm_thread.id, env->vm_thread.thread, fat_lock, type_name); _svmf_dump_stack_trace (env); } #endif } else { assert (fat_lock->owner == env); fat_lock->recursive_count++; /* overflow? */ if (fat_lock->recursive_count < 0) { /* restore recursive count */ fat_lock->recursive_count--; status = JNI_ERR; } } _svmm_mutex_unlock (); _svmm_resuming_java (env); _svmm_free_native_local (env, obj); if (status != JNI_OK) { _svmf_error_OutOfMemoryError (env); return JNI_ERR; } return JNI_OK; } } /* ---------------------------------------------------------------------- _svmf_enter_object_monitor_non_blocking ---------------------------------------------------------------------- */ inline svm_static jint _svmh_enter_object_monitor_non_blocking (_svmt_JNIEnv *env, _svmt_object_instance *instance, jboolean *succeeded) { _svmt_JavaVM *vm = env->vm; _svmt_word old_lockword; #ifndef NDEBUG const char *type_name = instance->vtable->type->name; #endif assert (instance != NULL); retry: old_lockword = instance->lockword; assert (env->thread_status != SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED); assert (env->thread_status != SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_DISALLOWED); { _svmt_word new_lockword = env->vm_thread.thinlock_id | _svmf_lockword_get_extra_bits (old_lockword); if (_svmm_compare_and_swap (instance->lockword, _svmf_lockword_get_extra_bits (old_lockword), new_lockword)) { /* thin lock acquired */ *succeeded = JNI_TRUE; return JNI_OK; } } /* Either the object is already locked, or the lock is inflated. */ if (_svmf_lockword_is_thin (old_lockword)) { /* it is a thin lock */ if (_svmf_lockword_get_thinlock_id (old_lockword) == env->vm_thread.thinlock_id) { /* the thinlock is already owned by the current thread */ jint recursive_count = _svmf_lockword_get_thinlock_recursive_count (old_lockword); assert (recursive_count <= SVM_THINLOCK_MAX_RECURSIVE_COUNT); if (++recursive_count <= SVM_THINLOCK_MAX_RECURSIVE_COUNT) { instance->lockword = _svmf_lockword_thinlock (env->vm_thread.thinlock_id, recursive_count, _svmf_lockword_get_extra_bits (old_lockword)); /* thin lock re-entered */ *succeeded = JNI_TRUE; return JNI_OK; } /* Overflow! We must inflate the lock. */ if (_svmf_inflate_lock_no_exception (env, instance) != JNI_OK) { _svmf_error_OutOfMemoryError (env); return JNI_ERR; } /* OK, retry locking with the new fat lock */ goto retry; } else { /* contention: the thinlock is owned by another thread */ *succeeded = JNI_FALSE; return JNI_OK; } } else { /* it is a fat lock */ _svmt_word fat_index = _svmf_lockword_get_fatlock_index (old_lockword); _svmt_fat_lock *fat_lock = vm->fat_locks.array[fat_index]; jint status = JNI_OK; jobject obj; if (_svmm_new_native_local (env, obj) != JNI_OK) { return JNI_ERR; } /* save reference in case GC happens */ *obj = instance; _svmm_stopping_java (env); /* IMPORTANT: From now on, we MUST NOT read or write any value which can be changed by GC or any other "stop the word" dependent operation. */ _svmm_mutex_lock (fat_lock->mutex); if (fat_lock->recursive_count == 0) { fat_lock->recursive_count = 1; fat_lock->owner = env; *succeeded = JNI_TRUE; #ifndef NDEBUG if (vm->verbose_synchronization) { /* dump a stack trace on fat lock acquisition */ _svmf_printf (env, stderr, "*** FAT LOCK ACQUIRED ***\n"); _svmf_printf (env, stderr, "Thread (%d,%d) aquires monitor %p of type %s\n", env->vm_thread.id, env->vm_thread.thread, fat_lock, type_name); _svmf_dump_stack_trace (env); } #endif } else if (fat_lock->owner == env) { assert (fat_lock->owner == env); fat_lock->recursive_count++; /* overflow? */ if (fat_lock->recursive_count < 0) { /* restore recursive count */ fat_lock->recursive_count--; status = JNI_ERR; } else { *succeeded = JNI_TRUE; } } else { *succeeded = JNI_FALSE; } _svmm_mutex_unlock (); _svmm_resuming_java (env); _svmm_free_native_local (env, obj); if (status != JNI_OK) { _svmf_error_OutOfMemoryError (env); return JNI_ERR; } return JNI_OK; } } /* ---------------------------------------------------------------------- _svmf_exit_object_monitor ---------------------------------------------------------------------- */ inline svm_static jint _svmf_exit_object_monitor (_svmt_JNIEnv *env, _svmt_object_instance *instance) { _svmt_JavaVM *vm = env->vm; _svmt_word old_lockword; assert (instance != NULL); old_lockword = instance->lockword; if (_svmf_lockword_is_thin (old_lockword)) { /* it is a thin lock */ jint recursive_count; if (_svmf_lockword_get_thinlock_id (old_lockword) != env->vm_thread.thinlock_id) { _svmf_error_IllegalMonitorStateException (env); return JNI_ERR; } recursive_count = _svmf_lockword_get_thinlock_recursive_count (old_lockword); if (recursive_count == 0) { /* we're releasing the thin lock */ instance->lockword = _svmf_lockword_get_extra_bits (old_lockword); goto handle_contention; } else { instance->lockword = _svmf_lockword_thinlock (env->vm_thread.thinlock_id, recursive_count - 1, _svmf_lockword_get_extra_bits (old_lockword)); /* we're done */ return JNI_OK; } } else { /* it is a fat lock */ _svmt_word fat_index = _svmf_lockword_get_fatlock_index (old_lockword); _svmt_fat_lock *fat_lock = vm->fat_locks.array[fat_index]; jint status = JNI_OK; jboolean released = JNI_FALSE; _svmm_mutex_lock (fat_lock->mutex); assert (fat_lock->recursive_count >= 0); if (fat_lock->recursive_count == 0 || fat_lock->owner != env) { status = JNI_ERR; } else { if (--(fat_lock->recursive_count) == 0) { released = JNI_TRUE; fat_lock->owner = NULL; _svmm_cond_broadcast (fat_lock->cond); } } _svmm_mutex_unlock (); if (status != JNI_OK) { _svmf_error_IllegalMonitorStateException (env); return JNI_ERR; } if (released) { goto handle_contention; } /* we're done */ return JNI_OK; } handle_contention: if (env->contention.owner.flag) { _svmt_JNIEnv *current; jint status = JNI_OK; _svmm_mutex_lock (env->contention.owner.mutex); current = env->contention.owner.wait_list; while (current != NULL) { if ((*(current->contention.requester.jobject)) != instance && _svmf_lockword_is_thin ((*(current->contention.requester.jobject))->lockword)) { assert (_svmf_lockword_get_thinlock_id ((*(current->contention.requester.jobject))-> lockword) == env->vm_thread.thinlock_id); if (_svmf_inflate_lock_no_exception (env, *(current->contention.requester.jobject)) != JNI_OK) { status = JNI_ERR; } } _svmm_cond_signal (current->contention.requester.cond); current = current->contention.requester.wait_list_next; } env->contention.owner.wait_list = NULL; env->contention.owner.flag = 0; _svmm_mutex_unlock (); if (status != JNI_OK) { _svmf_error_OutOfMemoryError (env); return JNI_ERR; } } /* we're done */ return JNI_OK; } /* ----------------------------------------------------- _svmf_thread_destroy: free memory used by the JNIEnv ----------------------------------------------------- */ svm_static jint _svmf_thread_destroy (_svmt_JNIEnv *env) { _svmt_native_ref *next_locref; _svmt_native_ref *current_locref; if (env == NULL) { return JNI_OK; } current_locref = env->native_locals.list; while (current_locref != NULL) { next_locref = current_locref->next; _svmm_gzfree_native_ref_no_exception (current_locref); current_locref = next_locref; } current_locref = env->native_locals.free_list; while (current_locref != NULL) { next_locref = current_locref->next; _svmm_gzfree_native_ref_no_exception (current_locref); current_locref = next_locref; } if (env->stack.start != NULL) { _svmm_free (env->stack.start); } _svmm_cond_destroy (env->wakeup_cond); _svmm_cond_destroy (env->suspension.cond); _svmm_cond_destroy (env->contention.requester.cond); _svmm_mutex_destroy (env->contention.owner.mutex); _svmm_gzfree_env_no_exception (env); return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_thread_recycle: put back the native local references in the global free_list and put the JNIEnv in the free list ---------------------------------------------------------------------- */ svm_static jint _svmf_thread_recycle (_svmt_JNIEnv *env) { _svmt_JavaVM *vm = env->vm; _svmt_native_ref *next_locref; _svmt_native_ref *current_locref; if (env == NULL) { return JNI_OK; } _svmm_mutex_lock (vm->global_mutex); current_locref = env->native_locals.list; while (current_locref != NULL) { next_locref = current_locref->next; /* put it in the global free_list */ current_locref->ref = NULL; current_locref->previous = NULL; current_locref->next = vm->native_globals.free_list; if (vm->native_globals.free_list != NULL) { vm->native_globals.free_list->previous = current_locref; } vm->native_globals.free_list = current_locref; current_locref = next_locref; } env->native_locals.list = NULL; current_locref = env->native_locals.free_list; while (current_locref != NULL) { next_locref = current_locref->next; /* put it in the global free_list */ current_locref->ref = NULL; current_locref->previous = NULL; current_locref->next = vm->native_globals.free_list; if (vm->native_globals.free_list != NULL) { vm->native_globals.free_list->previous = current_locref; } vm->native_globals.free_list = current_locref; current_locref = next_locref; } env->native_locals.free_list = NULL; if (env->stack.start != NULL) { _svmm_free (env->stack.start); env->stack.start = NULL; env->stack.end = NULL; env->stack.current_frame = NULL; } env->vm_thread.is_daemon = JNI_FALSE; env->vm_thread.sleeping_on_fat_lock = NULL; env->vm_thread.interrupted_status = SVM_THREAD_NOT_INTERRUPTED; env->critical_section_count = 0; env->status_flags = SVM_THREAD_STATUS_FLAGS_NONE; env->suspension.suspender_list = NULL; env->suspension.suspender_list_next = NULL; env->contention.owner.flag = 0; env->contention.owner.wait_list = NULL; env->contention.requester.wait_list_next = NULL; #ifndef NDEBUG memset (&(env->stop_resume), 0, sizeof (_svmt_stop_resume)); #endif /* The current thread has already been removed from the user or system thread list */ /* put the JNIEnv in the free_list */ env->previous = NULL; env->next = vm->threads.free_list; if (vm->threads.free_list != NULL) { vm->threads.free_list->previous = env; } vm->threads.free_list = env; _svmm_mutex_unlock (); return JNI_OK; } /* ---------------------------------------------------------------------- _svmf_vm_thread_start ---------------------------------------------------------------------- */ svm_static void * _svmf_vm_thread_start (void *_env) { _svmt_JNIEnv *env = (_svmt_JNIEnv *) _env; _svmt_JavaVM *vm = env->vm; assert (env->is_alive == JNI_TRUE); env->vm_thread.thread = _svmf_thread_self (); _svmf_set_current_env (env); _svmm_resuming_java (env); _svmm_invoke_static_virtualmachine_runthread (env); _svmm_mutex_lock (vm->global_mutex); _svmf_halt_if_requested (env); env->is_alive = JNI_FALSE; if (env->previous != NULL) { env->previous->next = env->next; } else { if (env->vm_thread.is_daemon) { vm->threads.system = env->next; } else { vm->threads.user = env->next; } } if (env->next != NULL) { env->next->previous = env->previous; } _svmm_cond_signal (vm->threads.vm_destruction_cond); _svmm_mutex_unlock (); _svmf_thread_recycle (env); return NULL; } /* ---------------------------------------------------------------------- _svmf_thread_native_start ---------------------------------------------------------------------- */ svm_static _svmt_JNIEnv * _svmf_thread_native_start (_svmt_JNIEnv *env, jobject threadInstance) { _svmt_JNIEnv *result = NULL; _svmt_JNIEnv *new_env = NULL; _svmt_JavaVM *vm = env->vm; { jint status = JNI_OK; _svmm_mutex_lock (vm->global_mutex); _svmf_halt_if_requested (env); if (vm->threads.free_list != NULL) { new_env = vm->threads.free_list; assert (new_env->previous == NULL); vm->threads.free_list = new_env->next; if (vm->threads.free_list != NULL) { vm->threads.free_list->previous = NULL; } new_env->next = vm->threads.user; vm->threads.user = new_env; if (new_env->next != NULL) { assert (new_env->next->previous == NULL); new_env->next->previous = new_env; } new_env->thread_status = SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED; } else if (vm->threads.next_thread_id <= SVM_MAX_THREAD_ID) { if (_svmm_gzalloc_env_no_exception (new_env) != JNI_OK) { goto unlock; } new_env->interface = &_svmv_native_interface; new_env->vm = vm; new_env->next = vm->threads.user; vm->threads.user = new_env; if (new_env->next != NULL) { assert (new_env->next->previous == NULL); new_env->next->previous = new_env; } new_env->vm_thread.interrupted_status = SVM_THREAD_NOT_INTERRUPTED; new_env->vm_thread.sleeping_on_fat_lock = NULL; new_env->vm_thread.id = vm->threads.next_thread_id++; new_env->vm_thread.thinlock_id = _svmf_get_initial_thinlock_id(new_env->vm_thread.id); vm->threads.array[new_env->vm_thread.id] = new_env; _svmm_cond_init (new_env->wakeup_cond); _svmm_cond_init (new_env->suspension.cond); _svmm_mutex_init (new_env->contention.owner.mutex); _svmm_cond_init (new_env->contention.requester.cond); new_env->thread_status = SVM_THREAD_STATUS_NOT_RUNNING_JAVA_RESUMING_ALLOWED; } else { status = JNI_ERR; } unlock: _svmm_mutex_unlock (); if (status != JNI_OK) { _svmf_error_OutOfMemoryError (env); goto end; } } if (_svmm_gzalloc_native_ref_no_exception (new_env->native_locals.list) != JNI_OK) { _svmf_thread_destroy (new_env); _svmf_error_OutOfMemoryError (env); goto end; } new_env->throwable = _svmf_cast_jobject_nref (new_env->native_locals.list); if (_svmm_gzalloc_native_ref_no_exception (new_env->native_locals.list->next) != JNI_OK) { _svmf_thread_destroy (new_env); _svmf_error_OutOfMemoryError (env); goto end; } new_env->vm_thread.thread_instance = _svmf_cast_jobject_nref (new_env->native_locals.list->next); *(new_env->vm_thread.thread_instance) = *(threadInstance); if (_svmm_gzalloc_native_ref_no_exception (new_env->native_locals.list->next->next) != JNI_OK) { _svmf_thread_destroy (new_env); _svmf_error_OutOfMemoryError (env); goto end; } new_env->contention.requester.jobject = _svmf_cast_jobject_nref (new_env->native_locals.list->next->next); if (_svmf_stack_init (new_env) != JNI_OK) { _svmf_thread_destroy (new_env); *(env->throwable) = *(new_env->throwable); goto end; } /* start it! */ new_env->is_alive = JNI_TRUE; if (_svmf_thread_create(&new_env->vm_thread.thread, &_svmf_vm_thread_start, new_env) != JNI_OK) { _svmf_thread_destroy (new_env); _svmf_error_InternalError (env); goto end; } /* it's started! */ result = new_env; end: return result; }